omarsol commited on
Commit
9816fb5
·
1 Parent(s): 6cd03bd

Tag LangSmith traces by deployment environment

Browse files
.env.example CHANGED
@@ -22,6 +22,9 @@ OPENROUTER_API_KEY=...
22
  LANGSMITH_API_KEY=...
23
  LANGSMITH_TRACING=true
24
  LANGSMITH_PROJECT=ai-tutor-app
 
 
 
25
 
26
  # To update documentation/add new course
27
  HF_TOKEN=... https://huggingface.co/settings/tokens to read or write to private DATASET repos e.g. https://huggingface.co/datasets/towardsai-tutors/ai-tutor-data/tree/main
 
22
  LANGSMITH_API_KEY=...
23
  LANGSMITH_TRACING=true
24
  LANGSMITH_PROJECT=ai-tutor-app
25
+ # Trace origin label (set HF Space variables to hf-dev or hf-prod; defaults to
26
+ # huggingface-space when SPACE_HOST exists, otherwise local)
27
+ # AI_TUTOR_DEPLOYMENT_ENV=local
28
 
29
  # To update documentation/add new course
30
  HF_TOKEN=... https://huggingface.co/settings/tokens to read or write to private DATASET repos e.g. https://huggingface.co/datasets/towardsai-tutors/ai-tutor-data/tree/main
app/agent_tracing.py CHANGED
@@ -3,6 +3,9 @@ from __future__ import annotations
3
  import os
4
 
5
  DEFAULT_LANGSMITH_PROJECT = "ai-tutor-app"
 
 
 
6
 
7
  TRUE_VALUES = {"1", "true", "t", "yes", "y", "on"}
8
  FALSE_VALUES = {"0", "false", "f", "no", "n", "off"}
@@ -27,6 +30,19 @@ def langsmith_tracing_enabled() -> bool:
27
  return bool(os.getenv("LANGSMITH_API_KEY"))
28
 
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  def configure_langsmith_environment() -> None:
31
  """Apply app defaults for LangSmith without requiring code changes in deploys."""
32
  if os.getenv("LANGSMITH_API_KEY") and os.getenv("LANGSMITH_TRACING") is None:
 
3
  import os
4
 
5
  DEFAULT_LANGSMITH_PROJECT = "ai-tutor-app"
6
+ DEFAULT_LOCAL_DEPLOYMENT_ENVIRONMENT = "local"
7
+ DEFAULT_HF_DEPLOYMENT_ENVIRONMENT = "huggingface-space"
8
+ LOCAL_DEPLOYMENT_HOST = "local"
9
 
10
  TRUE_VALUES = {"1", "true", "t", "yes", "y", "on"}
11
  FALSE_VALUES = {"0", "false", "f", "no", "n", "off"}
 
30
  return bool(os.getenv("LANGSMITH_API_KEY"))
31
 
32
 
33
+ def langsmith_deployment_identity() -> tuple[str, str]:
34
+ """Return stable environment and host labels for LangSmith traces."""
35
+ space_host = os.getenv("SPACE_HOST", "").strip()
36
+ environment = os.getenv("AI_TUTOR_DEPLOYMENT_ENV", "").strip()
37
+ if not environment:
38
+ environment = (
39
+ DEFAULT_HF_DEPLOYMENT_ENVIRONMENT
40
+ if space_host
41
+ else DEFAULT_LOCAL_DEPLOYMENT_ENVIRONMENT
42
+ )
43
+ return environment, space_host or LOCAL_DEPLOYMENT_HOST
44
+
45
+
46
  def configure_langsmith_environment() -> None:
47
  """Apply app defaults for LangSmith without requiring code changes in deploys."""
48
  if os.getenv("LANGSMITH_API_KEY") and os.getenv("LANGSMITH_TRACING") is None:
app/chat_service.py CHANGED
@@ -40,6 +40,7 @@ from langgraph.graph.message import REMOVE_ALL_MESSAGES
40
  from langgraph.store.memory import InMemoryStore
41
  from langgraph.types import Command
42
 
 
43
  from .chat_types import ChatEvent, ChatRequest, ChatTurn, SourceMatch
44
  from .deepseek_chat import TutorChatDeepSeek
45
  from .memory_presets import (
@@ -2651,6 +2652,7 @@ def agent_run_config(
2651
  memory_preset: str = "",
2652
  ) -> dict[str, Any]:
2653
  provider, actual_model = model_provider_and_name(request.model_name)
 
2654
  tools = effective_tool_names(
2655
  request.model_name,
2656
  request.enabled_tools,
@@ -2672,10 +2674,14 @@ def agent_run_config(
2672
  f"provider:{provider}",
2673
  f"model:{actual_model}",
2674
  f"memory:{preset}",
 
 
2675
  *(f"tool:{tool_name}" for tool_name in tools),
2676
  ],
2677
  "metadata": {
2678
  "app": "ai-tutor-app",
 
 
2679
  "thread_id": active_thread_id,
2680
  "conversation_id": active_thread_id,
2681
  "message_id": message_id,
 
40
  from langgraph.store.memory import InMemoryStore
41
  from langgraph.types import Command
42
 
43
+ from .agent_tracing import langsmith_deployment_identity
44
  from .chat_types import ChatEvent, ChatRequest, ChatTurn, SourceMatch
45
  from .deepseek_chat import TutorChatDeepSeek
46
  from .memory_presets import (
 
2652
  memory_preset: str = "",
2653
  ) -> dict[str, Any]:
2654
  provider, actual_model = model_provider_and_name(request.model_name)
2655
+ deployment_environment, deployment_host = langsmith_deployment_identity()
2656
  tools = effective_tool_names(
2657
  request.model_name,
2658
  request.enabled_tools,
 
2674
  f"provider:{provider}",
2675
  f"model:{actual_model}",
2676
  f"memory:{preset}",
2677
+ f"environment:{deployment_environment}",
2678
+ f"deployment:{deployment_host}",
2679
  *(f"tool:{tool_name}" for tool_name in tools),
2680
  ],
2681
  "metadata": {
2682
  "app": "ai-tutor-app",
2683
+ "environment": deployment_environment,
2684
+ "deployment_host": deployment_host,
2685
  "thread_id": active_thread_id,
2686
  "conversation_id": active_thread_id,
2687
  "message_id": message_id,
tests/test_agent_tracing.py CHANGED
@@ -7,6 +7,7 @@ from unittest.mock import patch
7
  from app.agent_tracing import (
8
  DEFAULT_LANGSMITH_PROJECT,
9
  configure_langsmith_environment,
 
10
  langsmith_tracing_enabled,
11
  parse_env_bool,
12
  )
@@ -40,6 +41,38 @@ class AgentTracingTestCase(unittest.TestCase):
40
  self.assertFalse(langsmith_tracing_enabled())
41
  self.assertNotIn("LANGSMITH_PROJECT", os.environ)
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def test_project_is_preserved(self) -> None:
44
  with patch.dict(
45
  os.environ,
 
7
  from app.agent_tracing import (
8
  DEFAULT_LANGSMITH_PROJECT,
9
  configure_langsmith_environment,
10
+ langsmith_deployment_identity,
11
  langsmith_tracing_enabled,
12
  parse_env_bool,
13
  )
 
41
  self.assertFalse(langsmith_tracing_enabled())
42
  self.assertNotIn("LANGSMITH_PROJECT", os.environ)
43
 
44
+ def test_deployment_identity_defaults_to_local(self) -> None:
45
+ with patch.dict(os.environ, {}, clear=True):
46
+ self.assertEqual(langsmith_deployment_identity(), ("local", "local"))
47
+
48
+ def test_deployment_identity_detects_hugging_face_space(self) -> None:
49
+ with patch.dict(
50
+ os.environ,
51
+ {"SPACE_HOST": "towardsai-tutors-ai-tutor-chatbot.hf.space"},
52
+ clear=True,
53
+ ):
54
+ self.assertEqual(
55
+ langsmith_deployment_identity(),
56
+ (
57
+ "huggingface-space",
58
+ "towardsai-tutors-ai-tutor-chatbot.hf.space",
59
+ ),
60
+ )
61
+
62
+ def test_explicit_deployment_environment_wins(self) -> None:
63
+ with patch.dict(
64
+ os.environ,
65
+ {
66
+ "AI_TUTOR_DEPLOYMENT_ENV": "hf-prod",
67
+ "SPACE_HOST": "towardsai-tutors-ai-tutor-chatbot.hf.space",
68
+ },
69
+ clear=True,
70
+ ):
71
+ self.assertEqual(
72
+ langsmith_deployment_identity(),
73
+ ("hf-prod", "towardsai-tutors-ai-tutor-chatbot.hf.space"),
74
+ )
75
+
76
  def test_project_is_preserved(self) -> None:
77
  with patch.dict(
78
  os.environ,
tests/test_chat_service.py CHANGED
@@ -413,17 +413,34 @@ class ChatServiceTestCase(unittest.TestCase):
413
  enabled_tools=("web_search",),
414
  )
415
 
416
- config = agent_run_config(request, "thread_123", "message_456")
 
 
 
 
 
 
 
417
 
418
  self.assertEqual(config["configurable"], {"thread_id": "thread_123"})
419
  self.assertEqual(config["run_name"], "ai-tutor-agent-turn")
420
  self.assertIn("provider:google-genai", config["tags"])
 
 
 
 
 
421
  self.assertIn("tool:retrieve_tutor_context", config["tags"])
422
  self.assertIn("tool:run_kb_command", config["tags"])
423
  self.assertIn("tool:google_search", config["tags"])
424
  self.assertEqual(config["metadata"]["thread_id"], "thread_123")
425
  self.assertEqual(config["metadata"]["conversation_id"], "thread_123")
426
  self.assertEqual(config["metadata"]["message_id"], "message_456")
 
 
 
 
 
427
  self.assertEqual(
428
  config["metadata"]["available_tools"],
429
  [
 
413
  enabled_tools=("web_search",),
414
  )
415
 
416
+ with patch.dict(
417
+ os.environ,
418
+ {
419
+ "AI_TUTOR_DEPLOYMENT_ENV": "hf-prod",
420
+ "SPACE_HOST": "towardsai-tutors-ai-tutor-chatbot.hf.space",
421
+ },
422
+ ):
423
+ config = agent_run_config(request, "thread_123", "message_456")
424
 
425
  self.assertEqual(config["configurable"], {"thread_id": "thread_123"})
426
  self.assertEqual(config["run_name"], "ai-tutor-agent-turn")
427
  self.assertIn("provider:google-genai", config["tags"])
428
+ self.assertIn("environment:hf-prod", config["tags"])
429
+ self.assertIn(
430
+ "deployment:towardsai-tutors-ai-tutor-chatbot.hf.space",
431
+ config["tags"],
432
+ )
433
  self.assertIn("tool:retrieve_tutor_context", config["tags"])
434
  self.assertIn("tool:run_kb_command", config["tags"])
435
  self.assertIn("tool:google_search", config["tags"])
436
  self.assertEqual(config["metadata"]["thread_id"], "thread_123")
437
  self.assertEqual(config["metadata"]["conversation_id"], "thread_123")
438
  self.assertEqual(config["metadata"]["message_id"], "message_456")
439
+ self.assertEqual(config["metadata"]["environment"], "hf-prod")
440
+ self.assertEqual(
441
+ config["metadata"]["deployment_host"],
442
+ "towardsai-tutors-ai-tutor-chatbot.hf.space",
443
+ )
444
  self.assertEqual(
445
  config["metadata"]["available_tools"],
446
  [