turingpulse-sdk-dspy 1.0.0__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.
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual environments
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
|
|
16
|
+
# Database files
|
|
17
|
+
*.db
|
|
18
|
+
*.sqlite3
|
|
19
|
+
|
|
20
|
+
# Environment variables
|
|
21
|
+
.env
|
|
22
|
+
.env.local
|
|
23
|
+
|
|
24
|
+
# IDE
|
|
25
|
+
.idea/
|
|
26
|
+
.vscode/
|
|
27
|
+
*.swp
|
|
28
|
+
*.swo
|
|
29
|
+
|
|
30
|
+
# Testing
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.coverage
|
|
33
|
+
htmlcov/
|
|
34
|
+
.tox/
|
|
35
|
+
|
|
36
|
+
# Logs
|
|
37
|
+
*.log
|
|
38
|
+
logs/
|
|
39
|
+
|
|
40
|
+
# OS files
|
|
41
|
+
.DS_Store
|
|
42
|
+
Thumbs.db
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: turingpulse-sdk-dspy
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: TuringPulse SDK integration for DSPy
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: dspy>=3.1.3
|
|
8
|
+
Requires-Dist: turingpulse-sdk>=1.0.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "turingpulse-sdk-dspy"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "TuringPulse SDK integration for DSPy"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"turingpulse-sdk>=1.0.0",
|
|
13
|
+
"dspy>=3.1.3",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.23"]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""DSPy instrumentation for TuringPulse.
|
|
2
|
+
|
|
3
|
+
Wraps DSPy module forward passes to capture LLM calls,
|
|
4
|
+
optimization traces, and retrieval steps.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from contextvars import ContextVar
|
|
11
|
+
from typing import Any, Dict, Optional, Sequence
|
|
12
|
+
|
|
13
|
+
from turingpulse_sdk import instrument, GovernanceDirective
|
|
14
|
+
from turingpulse_sdk.config import MAX_FIELD_SIZE
|
|
15
|
+
from turingpulse_sdk.context import current_context
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("turingpulse.sdk.dspy")
|
|
18
|
+
|
|
19
|
+
_INSTRUMENTING: ContextVar[bool] = ContextVar("_tp_dspy_instrumenting", default=False)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def instrument_dspy(
|
|
23
|
+
module,
|
|
24
|
+
*,
|
|
25
|
+
name: str,
|
|
26
|
+
governance: Optional[GovernanceDirective] = None,
|
|
27
|
+
model: Optional[str] = None,
|
|
28
|
+
provider: str = "openai",
|
|
29
|
+
kpis: Optional[Sequence["KPIConfig"]] = None,
|
|
30
|
+
metadata: Optional[Dict[str, str]] = None,
|
|
31
|
+
):
|
|
32
|
+
"""Wrap a DSPy module for TuringPulse observability.
|
|
33
|
+
|
|
34
|
+
Returns a callable wrapping ``module.forward()`` / ``module()``.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
module: A ``dspy.Module`` instance (e.g., ``dspy.ChainOfThought``).
|
|
38
|
+
name: Workflow display name.
|
|
39
|
+
governance: Optional governance directive.
|
|
40
|
+
model: LLM model name override.
|
|
41
|
+
provider: LLM provider name.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A callable wrapping the module's forward pass.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
@instrument(name=name, governance=governance, kpis=kpis, metadata=metadata or {})
|
|
48
|
+
def _run(*args: Any, **kwargs: Any) -> Any:
|
|
49
|
+
token = _INSTRUMENTING.set(True)
|
|
50
|
+
try:
|
|
51
|
+
result = module(*args, **kwargs)
|
|
52
|
+
|
|
53
|
+
ctx = current_context()
|
|
54
|
+
if ctx:
|
|
55
|
+
ctx.framework = "dspy"
|
|
56
|
+
ctx.node_type = "workflow"
|
|
57
|
+
|
|
58
|
+
ctx.set_io(
|
|
59
|
+
input_data=str(kwargs)[:MAX_FIELD_SIZE] if kwargs else str(args)[:MAX_FIELD_SIZE],
|
|
60
|
+
output_data=str(result)[:MAX_FIELD_SIZE],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if model:
|
|
64
|
+
ctx.set_model(model, provider)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
import dspy
|
|
68
|
+
lm = dspy.settings.lm
|
|
69
|
+
if lm is not None:
|
|
70
|
+
hist = getattr(lm, "history", [])
|
|
71
|
+
if hist:
|
|
72
|
+
total_prompt = 0
|
|
73
|
+
total_completion = 0
|
|
74
|
+
for entry in hist:
|
|
75
|
+
usage = None
|
|
76
|
+
if isinstance(entry, dict):
|
|
77
|
+
resp = entry.get("response")
|
|
78
|
+
if resp and hasattr(resp, "usage"):
|
|
79
|
+
usage = resp.usage
|
|
80
|
+
elif isinstance(resp, dict):
|
|
81
|
+
usage_d = resp.get("usage", {})
|
|
82
|
+
total_prompt += usage_d.get("prompt_tokens", 0) or 0
|
|
83
|
+
total_completion += usage_d.get("completion_tokens", 0) or 0
|
|
84
|
+
continue
|
|
85
|
+
if usage:
|
|
86
|
+
total_prompt += getattr(usage, "prompt_tokens", 0) or 0
|
|
87
|
+
total_completion += getattr(usage, "completion_tokens", 0) or 0
|
|
88
|
+
if total_prompt or total_completion:
|
|
89
|
+
ctx.set_tokens(total_prompt, total_completion)
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
return result
|
|
94
|
+
finally:
|
|
95
|
+
_INSTRUMENTING.reset(token)
|
|
96
|
+
|
|
97
|
+
return _run
|