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.
- {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/PKG-INFO +17 -2
- {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/README.md +5 -1
- {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/pyproject.toml +30 -3
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/__init__.py +1 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/__init__.py +28 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/logging_utils.py +152 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/otel_fastapi.py +70 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/trace_metadata.py +54 -0
- {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
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/README.md +85 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/__init__.py +5 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/app.py +287 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/config.py +39 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/servers/fileserver/models.py +122 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/__init__.py +3 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/context_tools.py +71 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/format_tools.py +27 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/tools/fserver_client_tools.py +53 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/__init__.py +0 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/format_utils.py +38 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/utils/fserver_client_utils.py +262 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/__init__.py +50 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/agents/__init__.py +22 -0
- {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
- {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
- {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
- {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
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/agents/invocation_utils.py +285 -0
- {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
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/config/dynagent_settings.py +87 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/llm/llm.py +36 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/services/__init__.py +28 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/services/context.py +227 -0
- {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
- {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
- {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
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/__init__.py +14 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/default_ui.py +91 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/ui/ui_utils.py +299 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/dynagent/utils/__init__.py +3 -0
- autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/py.typed +0 -0
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/__init__.py +0 -2
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/agents/__init__.py +0 -2
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/config/settings.py +0 -44
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/llm/llm.py +0 -12
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/observability/__init__.py +0 -9
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/services/__init__.py +0 -8
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/tools/format_tools.py +0 -48
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/__init__.py +0 -2
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/default_ui.py +0 -59
- autobots_devtools_shared_lib-0.1.2/src/autobots_devtools_shared_lib/dynagent/ui/ui_utils.py +0 -217
- {autobots_devtools_shared_lib-0.1.2 → autobots_devtools_shared_lib-0.1.4}/src/autobots_devtools_shared_lib/__init__.py +0 -0
- /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
- {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
- {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
- {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
- {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
- {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.
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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.
|
|
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
|
autobots_devtools_shared_lib-0.1.4/src/autobots_devtools_shared_lib/common/observability/__init__.py
ADDED
|
@@ -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.
|
|
9
|
+
from autobots_devtools_shared_lib.common.observability.logging_utils import get_logger
|
|
11
10
|
|
|
12
|
-
logger =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
```
|