autobots-devtools-shared-lib 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/PKG-INFO +17 -2
  2. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/README.md +5 -1
  3. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/pyproject.toml +30 -3
  4. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/__init__.py +1 -0
  5. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/__init__.py +28 -0
  6. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/logging_utils.py +152 -0
  7. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/otel_fastapi.py +70 -0
  8. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/trace_metadata.py +54 -0
  9. {autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent → autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common}/observability/tracing.py +17 -9
  10. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/README.md +85 -0
  11. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/__init__.py +5 -0
  12. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/app.py +287 -0
  13. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/config.py +39 -0
  14. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/models.py +122 -0
  15. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/__init__.py +3 -0
  16. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/context_tools.py +71 -0
  17. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/format_tools.py +27 -0
  18. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/fserver_client_tools.py +53 -0
  19. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/__init__.py +0 -0
  20. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/format_utils.py +38 -0
  21. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/fserver_client_utils.py +262 -0
  22. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/__init__.py +50 -0
  23. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/agents/__init__.py +22 -0
  24. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/agents/agent_config_utils.py +89 -11
  25. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/agents/agent_meta.py +4 -0
  26. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/agents/base_agent.py +12 -9
  27. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/agents/batch.py +41 -32
  28. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/agents/invocation_utils.py +285 -0
  29. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/agents/middleware.py +8 -8
  30. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/config/dynagent_settings.py +87 -0
  31. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/llm/llm.py +36 -0
  32. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/services/__init__.py +28 -0
  33. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/services/context.py +227 -0
  34. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/services/structured_converter.py +13 -31
  35. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/tools/state_tools.py +6 -22
  36. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/tools/tool_registry.py +30 -9
  37. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/__init__.py +14 -0
  38. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/default_ui.py +91 -0
  39. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/ui_utils.py +299 -0
  40. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/utils/__init__.py +3 -0
  41. autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/py.typed +0 -0
  42. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/__init__.py +0 -2
  43. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/agents/__init__.py +0 -2
  44. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/config/settings.py +0 -44
  45. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/llm/llm.py +0 -12
  46. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/observability/__init__.py +0 -9
  47. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/services/__init__.py +0 -8
  48. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/tools/format_tools.py +0 -48
  49. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/__init__.py +0 -2
  50. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/default_ui.py +0 -59
  51. autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/ui_utils.py +0 -217
  52. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/__init__.py +0 -0
  53. /autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/py.typed → /autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/__init__.py +0 -0
  54. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/config/__init__.py +0 -0
  55. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/llm/__init__.py +0 -0
  56. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/models/__init__.py +0 -0
  57. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/models/state.py +0 -0
  58. {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/dynagent/tools/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autobots-devtools-shared-lib
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Shared library functions to be used for all autobots projects
5
5
  License: MIT
6
6
  Author: Pralhad
@@ -12,10 +12,19 @@ Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Python :: 3.14
14
14
  Provides-Extra: dev
15
+ Provides-Extra: file-server
16
+ Provides-Extra: file-server-otel
15
17
  Requires-Dist: chainlit (>=2.9.6)
18
+ Requires-Dist: fastapi (>=0.115.0) ; extra == "dev"
19
+ Requires-Dist: fastapi (>=0.115.0) ; extra == "file-server"
16
20
  Requires-Dist: langchain (>=1.0.0)
21
+ Requires-Dist: langchain-anthropic (>=0.3.0)
17
22
  Requires-Dist: langchain-google-genai (>=4.2.0)
18
23
  Requires-Dist: langfuse (>=3.12.1)
24
+ Requires-Dist: opentelemetry-api (>=1.30.0,<2.0.0) ; extra == "file-server-otel"
25
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.30.0,<2.0.0) ; extra == "file-server-otel"
26
+ Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.49b0) ; extra == "file-server-otel"
27
+ Requires-Dist: opentelemetry-sdk (>=1.30.0,<2.0.0) ; extra == "file-server-otel"
19
28
  Requires-Dist: pre-commit (>=4.5.1) ; extra == "dev"
20
29
  Requires-Dist: pydantic-settings (>=2.10.1)
21
30
  Requires-Dist: pyright (>=1.1.408) ; extra == "dev"
@@ -25,6 +34,8 @@ Requires-Dist: pytest-cov (>=7.0.0) ; extra == "dev"
25
34
  Requires-Dist: python-dotenv (>=1.1.1)
26
35
  Requires-Dist: pyyaml (>=6.0.3)
27
36
  Requires-Dist: ruff (>=0.14.14) ; extra == "dev"
37
+ Requires-Dist: uvicorn[standard] (>=0.32.0) ; extra == "dev"
38
+ Requires-Dist: uvicorn[standard] (>=0.32.0) ; extra == "file-server"
28
39
  Description-Content-Type: text/markdown
29
40
 
30
41
  # Autobots DevTools Shared Library
@@ -182,6 +193,7 @@ make test-cov
182
193
  ```
183
194
 
184
195
  Tests are organized into:
196
+
185
197
  - **Unit tests** (`tests/unit/`): Test individual functions and classes
186
198
  - **Integration tests** (`tests/integration/`): Test component interactions
187
199
  - **E2E tests** (`tests/e2e/`): Test complete workflows
@@ -209,7 +221,10 @@ MIT
209
221
 
210
222
  ## Authors
211
223
 
212
- - Pralhad <pralhad@example.com>
224
+ - Pra1had
225
+ - Email: <pralhad.kamath@pratishthanventures.com>
226
+ - [Github:pra1had](https://github.com/pra1had)
227
+
213
228
 
214
229
  ## Questions?
215
230
 
@@ -153,6 +153,7 @@ make test-cov
153
153
  ```
154
154
 
155
155
  Tests are organized into:
156
+
156
157
  - **Unit tests** (`tests/unit/`): Test individual functions and classes
157
158
  - **Integration tests** (`tests/integration/`): Test component interactions
158
159
  - **E2E tests** (`tests/e2e/`): Test complete workflows
@@ -180,7 +181,10 @@ MIT
180
181
 
181
182
  ## Authors
182
183
 
183
- - Pralhad <pralhad@example.com>
184
+ - Pra1had
185
+ - Email: <pralhad.kamath@pratishthanventures.com>
186
+ - [Github:pra1had](https://github.com/pra1had)
187
+
184
188
 
185
189
  ## Questions?
186
190
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "autobots-devtools-shared-lib"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Shared library functions to be used for all autobots projects"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -11,6 +11,7 @@ requires-python = ">=3.12,<4.0.0"
11
11
  dependencies = [
12
12
  "chainlit>=2.9.6",
13
13
  "langchain>=1.0.0",
14
+ "langchain-anthropic>=0.3.0",
14
15
  "langchain-google-genai>=4.2.0",
15
16
  "langfuse>=3.12.1",
16
17
  "pydantic-settings>=2.10.1",
@@ -19,13 +20,25 @@ dependencies = [
19
20
  ]
20
21
 
21
22
  [project.optional-dependencies]
23
+ file-server = [
24
+ "fastapi>=0.115.0",
25
+ "uvicorn[standard]>=0.32.0",
26
+ ]
27
+ file-server-otel = [
28
+ "opentelemetry-api>=1.30.0,<2.0.0",
29
+ "opentelemetry-sdk>=1.30.0,<2.0.0",
30
+ "opentelemetry-exporter-otlp-proto-http>=1.30.0,<2.0.0",
31
+ "opentelemetry-instrumentation-fastapi>=0.49b0",
32
+ ]
22
33
  dev = [
34
+ "fastapi>=0.115.0",
23
35
  "pre-commit>=4.5.1",
24
36
  "pyright>=1.1.408",
25
37
  "pytest>=9.0.2",
26
38
  "pytest-asyncio>=1.3.0",
27
39
  "pytest-cov>=7.0.0",
28
40
  "ruff>=0.14.14",
41
+ "uvicorn[standard]>=0.32.0",
29
42
  ]
30
43
 
31
44
  [tool.poetry]
@@ -54,13 +67,27 @@ select = [
54
67
  "ARG", # flake8-unused-arguments
55
68
  "SIM", # flake8-simplify
56
69
  "S", # flake8-bandit (security)
70
+ "TCH", # flake8-type-checking
71
+ "PTH", # flake8-use-pathlib
72
+ "RET", # flake8-return
73
+ "TRY", # tryceratops
74
+ "PERF", # Perflint
75
+ "RUF", # Ruff-specific rules
57
76
  ]
58
77
  ignore = [
59
78
  "S101", # Allow assert in tests
79
+ "E501", # Line too long (handled by formatter)
80
+ "TRY003", # Avoid specifying long messages outside the exception class
60
81
  ]
61
82
 
62
83
  [tool.ruff.lint.per-file-ignores]
63
- "tests/**/*.py" = ["S101", "S106", "ARG001", "ARG002"]
84
+ "tests/**/*.py" = ["S101", "S106", "ARG001", "ARG002", "PLR2004"]
85
+
86
+ [tool.ruff.format]
87
+ quote-style = "double"
88
+ indent-style = "space"
89
+ skip-magic-trailing-comma = false
90
+ line-ending = "auto"
64
91
 
65
92
  [tool.pytest.ini_options]
66
93
  testpaths = ["tests"]
@@ -69,7 +96,7 @@ asyncio_default_fixture_loop_scope = "function"
69
96
  addopts = [
70
97
  "-v",
71
98
  "--tb=short",
72
- "--cov=autobots_devtools_shared_lib",
99
+ "--cov=src/autobots_devtools_shared_lib",
73
100
  "--cov-branch",
74
101
  "--cov-report=term-missing",
75
102
  "--cov-report=html",
@@ -0,0 +1 @@
1
+ # common: observability, tools, utils, servers
@@ -0,0 +1,28 @@
1
+ # ABOUTME: Observability module for bro-chat.
2
+ # ABOUTME: Provides Langfuse integration for LLM tracing and monitoring.
3
+
4
+ from autobots_devtools_shared_lib.common.observability.logging_utils import (
5
+ ConversationFilter,
6
+ get_agent_logger,
7
+ get_logger,
8
+ set_conversation_id,
9
+ set_log_level,
10
+ setup_logging,
11
+ )
12
+ from autobots_devtools_shared_lib.common.observability.trace_metadata import TraceMetadata
13
+ from autobots_devtools_shared_lib.common.observability.tracing import (
14
+ get_langfuse_handler,
15
+ init_tracing,
16
+ )
17
+
18
+ __all__ = [
19
+ "ConversationFilter",
20
+ "TraceMetadata",
21
+ "get_agent_logger",
22
+ "get_langfuse_handler",
23
+ "get_logger",
24
+ "init_tracing",
25
+ "set_conversation_id",
26
+ "set_log_level",
27
+ "setup_logging",
28
+ ]
@@ -0,0 +1,152 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from contextvars import ContextVar
5
+
6
+ LogLevelLike = int | str
7
+
8
+ _logging_configured: bool = False
9
+
10
+
11
+ class ConversationFilter(logging.Filter):
12
+ """
13
+ Inject a conversation/thread identifier into every log record.
14
+
15
+ This enables log format strings to include `%(conversation_id)s`
16
+ for tracing multi-message conversations across threads/tasks.
17
+ """
18
+
19
+ def filter(self, record: logging.LogRecord) -> bool: # type: ignore[override]
20
+ record.conversation_id = _conversation_id_var.get()
21
+ return True
22
+
23
+
24
+ # Thread-safe context for the current conversation/thread id.
25
+ _conversation_id_var: ContextVar[str] = ContextVar(
26
+ "conversation_id", default="default-conversation-id"
27
+ )
28
+
29
+
30
+ def set_conversation_id(thread_id: str) -> None:
31
+ """
32
+ Set the conversation/thread identifier for the current context.
33
+
34
+ Args:
35
+ thread_id: Identifier for the current conversation/thread.
36
+ """
37
+ _conversation_id_var.set(thread_id)
38
+
39
+
40
+ def _parse_log_level(level: LogLevelLike | None) -> int:
41
+ """
42
+ Normalize various level representations into a standard logging level.
43
+ """
44
+ if level is None:
45
+ return logging.INFO
46
+
47
+ if isinstance(level, int):
48
+ # Clamp to logging's supported range, but don't be too strict.
49
+ return max(0, min(50, level))
50
+
51
+ value = str(level).strip()
52
+ if not value:
53
+ return logging.INFO
54
+
55
+ # Numeric string (e.g. "10", "20")
56
+ if value.isdigit():
57
+ return _parse_log_level(int(value))
58
+
59
+ # Named level (DEBUG, INFO, etc.)
60
+ value = value.upper()
61
+ numeric = getattr(logging, value, None)
62
+ if isinstance(numeric, int):
63
+ return numeric
64
+
65
+ # Fallback
66
+ return logging.INFO
67
+
68
+
69
+ def setup_logging(
70
+ level: LogLevelLike | None = None,
71
+ fmt: str | None = None,
72
+ force: bool = False,
73
+ ) -> None:
74
+ """
75
+ Initialize process-wide logging configuration.
76
+
77
+ This is safe to call multiple times; configuration will only be applied once
78
+ unless `force=True` is passed.
79
+
80
+ Precedence for the log level:
81
+ 1. `level` argument (if provided)
82
+ 2. `LOG_LEVEL` environment variable
83
+ 3. Default: logging.INFO
84
+
85
+ Args:
86
+ level: Explicit log level (int or name like "DEBUG").
87
+ fmt: Log format string. If omitted, a default including conversation_id is used.
88
+ force: If True, reconfigure logging even if it was already configured.
89
+ """
90
+ global _logging_configured
91
+
92
+ if _logging_configured and not force:
93
+ return
94
+
95
+ # Determine effective level
96
+ env_level = os.getenv("LOG_LEVEL")
97
+ effective_level = _parse_log_level(level if level is not None else env_level)
98
+
99
+ # Default format includes conversation/thread context
100
+ log_format = fmt or (
101
+ "%(asctime)s - %(name)s - [%(conversation_id)s] - %(levelname)s - %(message)s"
102
+ )
103
+
104
+ root_logger = logging.getLogger()
105
+
106
+ if force or not _logging_configured:
107
+ # Clear existing handlers to avoid duplicate logs
108
+ for handler in list(root_logger.handlers):
109
+ root_logger.removeHandler(handler)
110
+
111
+ root_logger.setLevel(effective_level)
112
+
113
+ handler = logging.StreamHandler(sys.stdout)
114
+ handler.setLevel(effective_level)
115
+ handler.setFormatter(logging.Formatter(log_format))
116
+ handler.addFilter(ConversationFilter())
117
+
118
+ root_logger.addHandler(handler)
119
+
120
+ _logging_configured = True
121
+
122
+
123
+ def get_logger(name: str) -> logging.Logger:
124
+ """
125
+ Get a named logger, ensuring logging is initialized first.
126
+ """
127
+ if not _logging_configured:
128
+ setup_logging()
129
+ return logging.getLogger(name)
130
+
131
+
132
+ def get_agent_logger(name: str) -> logging.Logger:
133
+ """
134
+ Semantic alias for agent-related loggers.
135
+
136
+ Equivalent to `get_logger(name)`, but keeps call sites self-documenting.
137
+ """
138
+ return get_logger(name)
139
+
140
+
141
+ def set_log_level(level: LogLevelLike) -> None:
142
+ """
143
+ Dynamically adjust the root logger's level at runtime.
144
+
145
+ Args:
146
+ level: New log level as int or string.
147
+ """
148
+ numeric_level = _parse_log_level(level)
149
+ root_logger = logging.getLogger()
150
+ root_logger.setLevel(numeric_level)
151
+ for handler in root_logger.handlers:
152
+ handler.setLevel(numeric_level)
@@ -0,0 +1,70 @@
1
+ """
2
+ OpenTelemetry support for FastAPI, exporting traces to Langfuse.
3
+
4
+ Requires opentelemetry-instrumentation-fastapi and related packages.
5
+ Enable by setting LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY (optional: LANGFUSE_HOST).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import base64
11
+ import os
12
+ import sys
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ from fastapi import FastAPI
17
+
18
+
19
+ def _configure_langfuse_otlp() -> bool:
20
+ """If Langfuse keys are set, configure env for OTLP export to Langfuse. Returns True if set."""
21
+ pk = os.getenv("LANGFUSE_PUBLIC_KEY", "").strip()
22
+ sk = os.getenv("LANGFUSE_SECRET_KEY", "").strip()
23
+ if not pk or not sk:
24
+ return False
25
+ host = os.getenv("LANGFUSE_BASE_URL")
26
+ endpoint = f"{host}/api/public/otel"
27
+ auth = base64.b64encode(f"{pk}:{sk}".encode()).decode()
28
+ os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = endpoint
29
+ os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {auth}"
30
+ return True
31
+
32
+
33
+ def instrument_fastapi(app: FastAPI) -> bool:
34
+ """
35
+ Instrument a FastAPI app with OpenTelemetry and export traces to Langfuse.
36
+
37
+ Enable by setting LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY in the environment.
38
+
39
+ Returns:
40
+ True if instrumentation was applied, False if Langfuse not configured or packages missing.
41
+ """
42
+ if not _configure_langfuse_otlp():
43
+ return False
44
+
45
+ try:
46
+ from opentelemetry import trace
47
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( # pyright: ignore[reportMissingImports]
48
+ OTLPSpanExporter,
49
+ )
50
+ from opentelemetry.instrumentation.fastapi import ( # pyright: ignore[reportMissingImports]
51
+ FastAPIInstrumentor,
52
+ )
53
+ from opentelemetry.sdk.resources import Resource
54
+ from opentelemetry.sdk.trace import TracerProvider
55
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
56
+ except ImportError:
57
+ return False
58
+
59
+ service_name = os.getenv("OTEL_SERVICE_NAME", "file-server")
60
+ resource = Resource.create({"service.name": service_name})
61
+ provider = TracerProvider(resource=resource)
62
+ exporter = OTLPSpanExporter()
63
+ provider.add_span_processor(BatchSpanProcessor(exporter))
64
+ trace.set_tracer_provider(provider)
65
+ FastAPIInstrumentor.instrument_app(
66
+ app,
67
+ excluded_urls="/health",
68
+ )
69
+ print("OTEL: Exporting traces to Langfuse.", file=sys.stderr)
70
+ return True
@@ -0,0 +1,54 @@
1
+ # ABOUTME: Metadata dataclass for tracing and observability correlation.
2
+ # ABOUTME: Encapsulates session IDs, user IDs, app names, and tags for Langfuse.
3
+
4
+ import uuid
5
+ from dataclasses import asdict, dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass
10
+ class TraceMetadata:
11
+ """Metadata for tracing and observability correlation.
12
+
13
+ Encapsulates all metadata needed for Langfuse tracing, including
14
+ session correlation, user identification, and application context.
15
+ """
16
+
17
+ session_id: str
18
+ app_name: str = "default"
19
+ user_id: str = "default"
20
+ tags: list[str] = field(default_factory=list)
21
+
22
+ @classmethod
23
+ def create(
24
+ cls,
25
+ session_id: str | None = None,
26
+ app_name: str = "default",
27
+ user_id: str = "default",
28
+ tags: list[str] | None = None,
29
+ ) -> "TraceMetadata":
30
+ """Create TraceMetadata with auto-generated session_id if needed."""
31
+ if session_id is None:
32
+ session_id = str(uuid.uuid4())
33
+ return cls(
34
+ session_id=session_id[:200], # Langfuse 200-char limit
35
+ app_name=app_name,
36
+ user_id=user_id,
37
+ tags=tags or [],
38
+ )
39
+
40
+ @classmethod
41
+ def from_dict(cls, data: dict[str, Any] | None) -> "TraceMetadata":
42
+ """Create from legacy trace_metadata dict for backward compatibility."""
43
+ if data is None:
44
+ return cls.create()
45
+ return cls.create(
46
+ session_id=data.get("session_id"),
47
+ app_name=data.get("app_name", "default"),
48
+ user_id=data.get("user_id", "default"),
49
+ tags=data.get("tags", []),
50
+ )
51
+
52
+ def to_dict(self) -> dict[str, Any]:
53
+ """Convert to dict for metadata merging."""
54
+ return asdict(self)
@@ -1,15 +1,14 @@
1
1
  # ABOUTME: Langfuse tracing integration for bro-chat.
2
2
  # ABOUTME: Sets up LLM observability using Langfuse's decorator-based API.
3
3
 
4
- import logging
5
4
  from typing import Any
6
5
 
7
6
  from langfuse import Langfuse
8
7
  from langfuse.langchain import CallbackHandler
9
8
 
10
- from autobots_devtools_shared_lib.dynagent.config.settings import get_settings
9
+ from autobots_devtools_shared_lib.common.observability.logging_utils import get_logger
11
10
 
12
- logger = logging.getLogger(__name__)
11
+ logger = get_logger(__name__)
13
12
 
14
13
  _langfuse_client: Langfuse | None = None
15
14
 
@@ -18,14 +17,18 @@ def init_tracing() -> bool:
18
17
  """
19
18
  Initialize Langfuse tracing.
20
19
 
21
- Reads configuration from environment via get_settings().
20
+ Reads configuration from environment via get_dynagent_settings().
22
21
 
23
22
  Returns:
24
23
  True if tracing was initialized successfully, False otherwise.
25
24
  """
26
25
  global _langfuse_client
27
26
 
28
- settings = get_settings()
27
+ from autobots_devtools_shared_lib.dynagent.config.dynagent_settings import (
28
+ get_dynagent_settings,
29
+ )
30
+
31
+ settings = get_dynagent_settings()
29
32
 
30
33
  if not settings.is_langfuse_configured():
31
34
  logger.info("Langfuse not configured, tracing disabled")
@@ -38,13 +41,14 @@ def init_tracing() -> bool:
38
41
  host=settings.langfuse_host,
39
42
  )
40
43
 
41
- logger.info("Langfuse tracing initialized successfully")
42
- return True
43
-
44
44
  except Exception as e:
45
45
  logger.warning(f"Failed to initialize Langfuse tracing: {e}")
46
46
  return False
47
47
 
48
+ else:
49
+ logger.info("Langfuse tracing initialized successfully")
50
+ return True
51
+
48
52
 
49
53
  def get_langfuse_handler() -> CallbackHandler | None:
50
54
  """
@@ -56,7 +60,11 @@ def get_langfuse_handler() -> CallbackHandler | None:
56
60
  if _langfuse_client is None:
57
61
  return None
58
62
 
59
- settings = get_settings()
63
+ from autobots_devtools_shared_lib.dynagent.config.dynagent_settings import (
64
+ get_dynagent_settings,
65
+ )
66
+
67
+ settings = get_dynagent_settings()
60
68
  return CallbackHandler(public_key=settings.langfuse_public_key)
61
69
 
62
70
 
@@ -0,0 +1,85 @@
1
+ # File Server
2
+
3
+ REST server that implements the API used by `fserver_client` (listFiles, readFile, writeFile, moveFile, createDownloadLink, health).
4
+
5
+ ## Setup
6
+
7
+ From the repo root (or `autobots-devtools-shared-lib`):
8
+
9
+ ```bash
10
+ pip install -e ".[file-server]"
11
+ # With OpenTelemetry (Langfuse): pip install -e ".[file-server,file-server-otel]"
12
+ # Or with dev deps: pip install -e ".[dev]"
13
+ ```
14
+
15
+ ## Run
16
+
17
+ From `autobots-devtools-shared-lib`:
18
+
19
+ ```bash
20
+ make file-server
21
+ ```
22
+
23
+ Or with uvicorn directly (port 9002 is the default used by `fserver_client`):
24
+
25
+ ```bash
26
+ uvicorn autobots_devtools_shared_lib.common.servers.fileserver.app:app --reload --host 0.0.0.0 --port 9002
27
+ ```
28
+
29
+ ## Environment (optional)
30
+
31
+ | Variable | Default | Description |
32
+ |----------|---------|-------------|
33
+ | `FILE_SERVER_ROOT` | `.` | Directory to serve (resolved). |
34
+ | `FILE_SERVER_HOST` | `0.0.0.0` | Bind host. |
35
+ | `FILE_SERVER_PORT` | `9002` | Bind port. |
36
+ | `FILE_SERVER_MAX_FILE_SIZE_MB` | `0` | Max upload size in MB (0 = no limit). |
37
+ | `FILE_SERVER_ENABLE_CORS` | (off) | Set to `1` or `true` to enable CORS. |
38
+ | `FILE_SERVER_CORS_ORIGINS` | `*` | Comma-separated origins when CORS enabled. |
39
+ | `LANGFUSE_PUBLIC_KEY` | — | Langfuse project public key; with `LANGFUSE_SECRET_KEY`, traces are sent to Langfuse. |
40
+ | `LANGFUSE_SECRET_KEY` | — | Langfuse project secret key. |
41
+ | `LANGFUSE_HOST` | - | Langfuse base URL |
42
+ | `OTEL_SERVICE_NAME` | `file-server` | Service name shown in Langfuse for this server. |
43
+
44
+ When `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` are set and `opentelemetry-instrumentation-fastapi` is installed (e.g. via `[file-server-otel]`), the app is instrumented and HTTP request traces are sent to Langfuse. `/health` is excluded from spans.
45
+
46
+ ## Tracing (Langfuse)
47
+
48
+ 1. In `.env` set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` (optionally `LANGFUSE_HOST`).
49
+ 2. Install the OTEL extra into the venv that Make uses: `make install-file-server-otel`.
50
+ 3. Run: `make file-server-otel`.
51
+ 4. Send requests to the file server; open your Langfuse project to see traces (service: `file-server`).
52
+
53
+ If you see a warning about packages not installed, run `make install-file-server-otel` from `autobots-devtools-shared-lib` (Make uses `../.venv`). Spans appear in Langfuse when you send requests (e.g. open http://localhost:9002/docs and call an endpoint).
54
+
55
+ ## Test the client tools
56
+
57
+ With the server running and `FILE_SERVER_HOST=localhost` (or unset, since localhost is the default):
58
+
59
+ ```python
60
+ import json
61
+
62
+ from autobots_devtools_shared_lib.common.tools.fserver_client_tools import (
63
+ list_files,
64
+ read_file,
65
+ write_file,
66
+ get_disk_usage,
67
+ )
68
+
69
+ # Optional workspace context (any product-specific keys)
70
+ workspace_context = json.dumps(
71
+ {
72
+ "agent_name": "my-agent",
73
+ "user_name": "dev",
74
+ "repo_name": "my-repo",
75
+ "jira_number": "JIRA-1",
76
+ }
77
+ )
78
+
79
+ write_file.invoke(
80
+ {"file_name": "hello.txt", "content": "Hello world", "workspace_context": workspace_context}
81
+ )
82
+ print(list_files.invoke({"base_path": "", "workspace_context": workspace_context}))
83
+ print(read_file.invoke({"file_name": "hello.txt", "workspace_context": workspace_context}))
84
+ print(get_disk_usage.invoke({}))
85
+ ```
@@ -0,0 +1,5 @@
1
+ """File server package (config, models, FastAPI app)."""
2
+
3
+ from .app import app # re-export for convenience
4
+
5
+ __all__ = ["app"]