decimalai 0.3.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.
Files changed (54) hide show
  1. decimalai-0.3.0/.github/workflows/ci.yml +46 -0
  2. decimalai-0.3.0/.github/workflows/publish.yml +65 -0
  3. decimalai-0.3.0/.gitignore +37 -0
  4. decimalai-0.3.0/LICENSE +0 -0
  5. decimalai-0.3.0/PKG-INFO +78 -0
  6. decimalai-0.3.0/README.md +41 -0
  7. decimalai-0.3.0/decimalai/__init__.py +298 -0
  8. decimalai-0.3.0/decimalai/_client.py +334 -0
  9. decimalai-0.3.0/decimalai/_config.py +149 -0
  10. decimalai-0.3.0/decimalai/autogen.py +40 -0
  11. decimalai-0.3.0/decimalai/cli/__init__.py +0 -0
  12. decimalai-0.3.0/decimalai/cli/dataset_cmd.py +0 -0
  13. decimalai-0.3.0/decimalai/cli/main.py +75 -0
  14. decimalai-0.3.0/decimalai/cli/manifest_cmd.py +0 -0
  15. decimalai-0.3.0/decimalai/cli/replay_cmd.py +0 -0
  16. decimalai-0.3.0/decimalai/evals/__init__.py +557 -0
  17. decimalai-0.3.0/decimalai/evals/adapters.py +208 -0
  18. decimalai-0.3.0/decimalai/evals/builtin.py +84 -0
  19. decimalai-0.3.0/decimalai/export/__init__.py +0 -0
  20. decimalai-0.3.0/decimalai/export/jsonl.py +0 -0
  21. decimalai-0.3.0/decimalai/export/parquet.py +0 -0
  22. decimalai-0.3.0/decimalai/generic.py +338 -0
  23. decimalai-0.3.0/decimalai/integrations/__init__.py +5 -0
  24. decimalai-0.3.0/decimalai/integrations/_lc_compat.py +340 -0
  25. decimalai-0.3.0/decimalai/integrations/langgraph.py +0 -0
  26. decimalai-0.3.0/decimalai/integrations/openai.py +0 -0
  27. decimalai-0.3.0/decimalai/integrations/otel.py +297 -0
  28. decimalai-0.3.0/decimalai/langchain.py +842 -0
  29. decimalai-0.3.0/decimalai/llamaindex.py +40 -0
  30. decimalai-0.3.0/decimalai/manifest/__init__.py +0 -0
  31. decimalai-0.3.0/decimalai/manifest/detector.py +0 -0
  32. decimalai-0.3.0/decimalai/manifest/extractor.py +0 -0
  33. decimalai-0.3.0/decimalai/manifest/hasher.py +0 -0
  34. decimalai-0.3.0/decimalai/openai_agents.py +42 -0
  35. decimalai-0.3.0/decimalai/replay/__init__.py +0 -0
  36. decimalai-0.3.0/decimalai/replay/tasks.py +0 -0
  37. decimalai-0.3.0/decimalai/schema/__init__.py +16 -0
  38. decimalai-0.3.0/decimalai/schema/common.py +55 -0
  39. decimalai-0.3.0/decimalai/schema/dataset.py +0 -0
  40. decimalai-0.3.0/decimalai/schema/manifest.py +306 -0
  41. decimalai-0.3.0/decimalai/schema/trace.py +110 -0
  42. decimalai-0.3.0/examples/demo_mock_agent.py +561 -0
  43. decimalai-0.3.0/examples/demo_real_agent.py +299 -0
  44. decimalai-0.3.0/pyproject.toml +44 -0
  45. decimalai-0.3.0/pyrightconfig.json +11 -0
  46. decimalai-0.3.0/tests/test_audit_improvements.py +455 -0
  47. decimalai-0.3.0/tests/test_cli.py +39 -0
  48. decimalai-0.3.0/tests/test_client.py +124 -0
  49. decimalai-0.3.0/tests/test_future_proofing.py +386 -0
  50. decimalai-0.3.0/tests/test_langchain_compat.py +474 -0
  51. decimalai-0.3.0/tests/test_manifest.py +232 -0
  52. decimalai-0.3.0/tests/test_rate_limit_handling.py +164 -0
  53. decimalai-0.3.0/tests/test_sdk.py +274 -0
  54. decimalai-0.3.0/tests/test_sdk_core.py +428 -0
@@ -0,0 +1,46 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test (Python ${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.9", "3.11", "3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: pip install -e ".[dev]"
27
+
28
+ - name: Run tests
29
+ run: pytest tests/ -v
30
+
31
+ lint:
32
+ name: Lint
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Set up Python
38
+ uses: actions/setup-python@v5
39
+ with:
40
+ python-version: "3.12"
41
+
42
+ - name: Install dependencies
43
+ run: pip install ruff
44
+
45
+ - name: Check formatting
46
+ run: ruff check decimalai/ --select I,E,W,F --ignore E501
@@ -0,0 +1,65 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write # Required for Trusted Publisher (OIDC)
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: Run Tests
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.9", "3.11", "3.12"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ pip install -e ".[dev]"
29
+
30
+ - name: Run tests
31
+ run: pytest tests/ -v
32
+
33
+ publish:
34
+ name: Publish to PyPI
35
+ needs: test # Only publish if tests pass
36
+ runs-on: ubuntu-latest
37
+ environment: pypi # Optional: use GitHub Environment for approvals
38
+
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - name: Set up Python
43
+ uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.12"
46
+
47
+ - name: Install build tools
48
+ run: pip install build
49
+
50
+ - name: Build package
51
+ run: python -m build
52
+
53
+ - name: Verify package version matches release tag
54
+ run: |
55
+ PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
56
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
57
+ if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
58
+ echo "❌ Version mismatch: pyproject.toml=$PKG_VERSION, tag=$TAG_VERSION"
59
+ exit 1
60
+ fi
61
+ echo "✅ Version match: $PKG_VERSION"
62
+
63
+ - name: Publish to PyPI
64
+ uses: pypa/gh-action-pypi-publish@release/v1
65
+ # No API token needed — uses Trusted Publisher (OIDC)
@@ -0,0 +1,37 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg-info/
7
+ *.egg
8
+ dist/
9
+ build/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+ *~
23
+
24
+ # Testing
25
+ .pytest_cache/
26
+ .coverage
27
+ htmlcov/
28
+ .mypy_cache/
29
+
30
+ # OS
31
+ .DS_Store
32
+ Thumbs.db
33
+
34
+ # Env
35
+ .env
36
+ .env.*
37
+ !.env.example
File without changes
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: decimalai
3
+ Version: 0.3.0
4
+ Summary: Open source SDK for agent dataset lifecycle management
5
+ Project-URL: Homepage, https://github.com/decimal-labs/decimalai-python
6
+ Project-URL: Documentation, https://docs.decimal.ai
7
+ Project-URL: Repository, https://github.com/decimal-labs/decimalai-python
8
+ Author: Decimal AI
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.9
12
+ Requires-Dist: click>=8.0
13
+ Requires-Dist: httpx>=0.24
14
+ Requires-Dist: pydantic>=2.0
15
+ Provides-Extra: all
16
+ Requires-Dist: langchain-core>=0.3; extra == 'all'
17
+ Requires-Dist: langgraph>=0.1; extra == 'all'
18
+ Requires-Dist: openai>=1.0; extra == 'all'
19
+ Provides-Extra: dev
20
+ Requires-Dist: langchain-core>=0.3; extra == 'dev'
21
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
22
+ Requires-Dist: pytest-mock>=3.0; extra == 'dev'
23
+ Requires-Dist: pytest>=7.0; extra == 'dev'
24
+ Requires-Dist: responses>=0.23; extra == 'dev'
25
+ Provides-Extra: langchain
26
+ Requires-Dist: langchain-core>=0.3; extra == 'langchain'
27
+ Provides-Extra: langgraph
28
+ Requires-Dist: langgraph>=0.1; extra == 'langgraph'
29
+ Provides-Extra: openai
30
+ Requires-Dist: openai>=1.0; extra == 'openai'
31
+ Provides-Extra: openai-agents
32
+ Requires-Dist: openai-agents>=0.1; extra == 'openai-agents'
33
+ Provides-Extra: otel
34
+ Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
35
+ Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # DecimalAI Python SDK
39
+
40
+ The open source SDK for [Decimal AI](https://decimal.ai) — the dataset control plane for agents.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install decimal-ai
46
+ ```
47
+
48
+ With framework integrations:
49
+ ```bash
50
+ pip install "decimal-ai[langchain]" # LangChain/LangGraph
51
+ pip install "decimal-ai[openai]" # OpenAI SDK
52
+ pip install "decimal-ai[all]" # All integrations
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```python
58
+ import decimalai
59
+
60
+ decimalai.init(api_key="dai_sk_...")
61
+
62
+ # LangChain/LangGraph — add callback handler
63
+ from decimalai.langchain import CallbackHandler
64
+
65
+ llm = ChatOpenAI(model="gpt-4o", callbacks=[CallbackHandler()])
66
+ ```
67
+
68
+ ## What It Does
69
+
70
+ - **Trace capture** — Auto-captures LLM calls, tool calls, and agent steps
71
+ - **Version spec extraction** — Auto-detects tool schemas, prompts, models, and graph topology
72
+ - **Change detection** — Detects when your agent configuration changes
73
+ - **Replay helpers** — Pull and submit replay tasks for data rescue
74
+ - **Dataset export** — Export clean JSONL/parquet for fine-tuning
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,41 @@
1
+ # DecimalAI Python SDK
2
+
3
+ The open source SDK for [Decimal AI](https://decimal.ai) — the dataset control plane for agents.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install decimal-ai
9
+ ```
10
+
11
+ With framework integrations:
12
+ ```bash
13
+ pip install "decimal-ai[langchain]" # LangChain/LangGraph
14
+ pip install "decimal-ai[openai]" # OpenAI SDK
15
+ pip install "decimal-ai[all]" # All integrations
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ import decimalai
22
+
23
+ decimalai.init(api_key="dai_sk_...")
24
+
25
+ # LangChain/LangGraph — add callback handler
26
+ from decimalai.langchain import CallbackHandler
27
+
28
+ llm = ChatOpenAI(model="gpt-4o", callbacks=[CallbackHandler()])
29
+ ```
30
+
31
+ ## What It Does
32
+
33
+ - **Trace capture** — Auto-captures LLM calls, tool calls, and agent steps
34
+ - **Version spec extraction** — Auto-detects tool schemas, prompts, models, and graph topology
35
+ - **Change detection** — Detects when your agent configuration changes
36
+ - **Replay helpers** — Pull and submit replay tasks for data rescue
37
+ - **Dataset export** — Export clean JSONL/parquet for fine-tuning
38
+
39
+ ## License
40
+
41
+ MIT
@@ -0,0 +1,298 @@
1
+ """DecimalAI SDK — Agent dataset lifecycle platform.
2
+
3
+ Quick start::
4
+
5
+ import decimalai
6
+ decimalai.init() # reads DECIMAL_API_KEY from env
7
+
8
+ # LangChain — one-liner
9
+ decimalai.init(langchain=True)
10
+
11
+ # Or manual install
12
+ from decimalai.langchain import install
13
+ install()
14
+
15
+ # Generic / any framework
16
+ @decimalai.trace(agent_name="my-agent")
17
+ def run_agent(query):
18
+ decimalai.log_llm_call(model="gpt-4o", input=msgs, output=resp)
19
+ return resp.choices[0].message.content
20
+ """
21
+
22
+ __version__ = "0.3.0"
23
+
24
+ import logging
25
+ import os
26
+ from typing import Optional
27
+
28
+ logger = logging.getLogger("decimalai")
29
+
30
+
31
+ def init(
32
+ api_key: Optional[str] = None,
33
+ base_url: Optional[str] = None,
34
+ project: Optional[str] = None,
35
+ enabled: bool = True,
36
+ langchain: bool = False,
37
+ agent_name: Optional[str] = None,
38
+ ) -> None:
39
+ """Initialize the DecimalAI SDK.
40
+
41
+ Must be called once before using any integration. Configuration is
42
+ resolved in order: explicit parameter → environment variable → default.
43
+
44
+ Args:
45
+ api_key: API key. Falls back to ``DECIMAL_API_KEY`` env var.
46
+ base_url: Backend URL. Falls back to ``DECIMAL_BASE_URL``, then
47
+ ``https://api.decimal.ai``.
48
+ project: Optional project grouping.
49
+ enabled: Set ``False`` to disable all tracing (integrations become no-ops).
50
+ langchain: If ``True``, auto-calls ``decimalai.langchain.install()``.
51
+ agent_name: Default agent name for langchain auto-install.
52
+
53
+ Raises:
54
+ DecimalConfigError: If ``api_key`` is not provided and not in env.
55
+ """
56
+ from ._config import DecimalConfig, DecimalConfigError
57
+ from ._client import DecimalAIClient
58
+
59
+ import decimalai._config as _cfg
60
+
61
+ # Resolve API key
62
+ resolved_key = api_key or os.environ.get("DECIMAL_API_KEY", "")
63
+ if not resolved_key and enabled:
64
+ raise DecimalConfigError(
65
+ "No API key provided. Pass api_key= to decimalai.init() "
66
+ "or set the DECIMAL_API_KEY environment variable."
67
+ )
68
+
69
+ # Resolve base URL
70
+ resolved_url = (
71
+ base_url
72
+ or os.environ.get("DECIMAL_BASE_URL", "")
73
+ or "https://api.decimal.ai"
74
+ )
75
+
76
+ config = DecimalConfig(
77
+ api_key=resolved_key,
78
+ base_url=resolved_url.rstrip("/"),
79
+ project=project,
80
+ enabled=enabled,
81
+ )
82
+ _cfg._config = config
83
+
84
+ if enabled:
85
+ _cfg._client = DecimalAIClient(
86
+ api_key=config.api_key,
87
+ base_url=config.base_url,
88
+ project=config.project,
89
+ )
90
+ logger.info(
91
+ "DecimalAI SDK initialized: base_url=%s project=%s",
92
+ config.base_url,
93
+ config.project,
94
+ )
95
+ else:
96
+ _cfg._client = None
97
+ logger.info("DecimalAI SDK initialized in disabled mode (no-op)")
98
+
99
+ # Auto-install langchain tracing if requested
100
+ if langchain:
101
+ from .langchain import install as _lc_install
102
+ _lc_install(agent_name=agent_name)
103
+
104
+
105
+ def send(trace) -> None:
106
+ """Manually send a trace to the backend.
107
+
108
+ For advanced usage when ``auto_send=False``.
109
+ """
110
+ from ._config import _get_client
111
+
112
+ client = _get_client()
113
+ client.ingest_trace(trace)
114
+
115
+
116
+ # ── Eval convenience functions ─────────────────────────────────
117
+
118
+
119
+ def eval(
120
+ trace_id: str,
121
+ name: str,
122
+ score: float,
123
+ *,
124
+ source: str = "custom",
125
+ source_label: Optional[str] = None,
126
+ passed: Optional[bool] = None,
127
+ reason: Optional[str] = None,
128
+ category: str = "quality",
129
+ ) -> dict:
130
+ """Push a single eval score to a trace.
131
+
132
+ This is the simplest way to attach evaluation results to a trace.
133
+ All scores are visible in the dashboard's Evaluation Breakdown card
134
+ grouped by source.
135
+
136
+ Args:
137
+ trace_id: The trace to attach the score to.
138
+ name: Metric name (e.g., "factual_accuracy", "coherence").
139
+ score: Score value between 0.0 and 1.0.
140
+ source: Eval source identifier. Defaults to "custom".
141
+ source_label: Human-readable display name (e.g., "My RAG Eval").
142
+ passed: Binary pass/fail. Defaults to score >= 0.5.
143
+ reason: Human-readable explanation of the score.
144
+ category: "quality" (default) or "compatibility".
145
+
146
+ Returns:
147
+ API response with stored score details and recomputed verdict.
148
+
149
+ Example::
150
+
151
+ import decimalai
152
+ decimalai.init()
153
+
154
+ decimalai.eval(
155
+ trace_id="abc123",
156
+ name="factual_accuracy",
157
+ score=0.75,
158
+ reason="3/4 facts verified against source docs",
159
+ )
160
+ """
161
+ from ._config import _get_client
162
+
163
+ client = _get_client()
164
+ score_entry = {
165
+ "name": name,
166
+ "score": score,
167
+ "passed": passed if passed is not None else score >= 0.5,
168
+ }
169
+ if reason:
170
+ score_entry["reason"] = reason
171
+
172
+ metadata = {}
173
+ if source_label:
174
+ metadata["source_label"] = source_label
175
+
176
+ return client.push_eval_scores(
177
+ trace_id=trace_id,
178
+ source=source,
179
+ scores=[score_entry],
180
+ )
181
+
182
+
183
+ def score(
184
+ trace_id: str,
185
+ name: str,
186
+ value: float,
187
+ reason: Optional[str] = None,
188
+ ) -> dict:
189
+ """Shorthand for pushing a single eval score.
190
+
191
+ Args:
192
+ trace_id: The trace to attach the score to.
193
+ name: Metric name.
194
+ value: Score between 0.0 and 1.0.
195
+ reason: Optional explanation.
196
+
197
+ Returns:
198
+ API response.
199
+
200
+ Example::
201
+
202
+ decimalai.score("abc123", "factual_accuracy", 0.75)
203
+ """
204
+ return eval(
205
+ trace_id=trace_id,
206
+ name=name,
207
+ score=value,
208
+ reason=reason,
209
+ )
210
+
211
+
212
+ def get_eval_breakdown(trace_id: str) -> dict:
213
+ """Get the full eval breakdown for a trace with provenance info.
214
+
215
+ Returns scores grouped by source (Manifest Diff, DeepEval, LangSmith,
216
+ Custom, etc.) with icons, labels, and decision reasons.
217
+
218
+ Returns:
219
+ Dict with eval_verdict, quality_avg, compat_avg, source_groups,
220
+ and decision_reasons.
221
+
222
+ Example::
223
+
224
+ import decimalai
225
+ decimalai.init()
226
+
227
+ bd = decimalai.get_eval_breakdown("abc123")
228
+ print(f"Verdict: {bd['eval_verdict']}")
229
+ """
230
+ from ._config import _get_client
231
+
232
+ client = _get_client()
233
+ return client.get_eval_breakdown(trace_id)
234
+
235
+
236
+ # ── Re-export generic tracing API ──────────────────────────────
237
+
238
+ from .generic import ( # noqa: E402, F401
239
+ log_llm_call,
240
+ log_tool_call,
241
+ start_trace,
242
+ trace,
243
+ )
244
+
245
+ # Re-export eval adapters from new location
246
+ from .evals.adapters import ( # noqa: E402, F401
247
+ push_deepeval_results,
248
+ push_langsmith_scores,
249
+ push_custom_scores,
250
+ )
251
+
252
+ from .evals import batch_eval # noqa: E402, F401
253
+
254
+ __all__ = [
255
+ "__version__",
256
+ "init",
257
+ "send",
258
+ "eval",
259
+ "score",
260
+ "get_eval_breakdown",
261
+ "trace",
262
+ "start_trace",
263
+ "log_llm_call",
264
+ "log_tool_call",
265
+ "push_deepeval_results",
266
+ "push_langsmith_scores",
267
+ "push_custom_scores",
268
+ "batch_eval",
269
+ ]
270
+
271
+
272
+ # ── Auto-init from environment variable ────────────────────────
273
+ # Setting DECIMAL_AUTO_TRACE=langchain will auto-init and install tracing.
274
+
275
+ def _auto_init_from_env() -> None:
276
+ """Auto-initialize from environment variables if configured."""
277
+ auto_trace = os.environ.get("DECIMAL_AUTO_TRACE", "").strip().lower()
278
+ if not auto_trace:
279
+ return
280
+
281
+ api_key = os.environ.get("DECIMAL_API_KEY", "")
282
+ if not api_key:
283
+ logger.debug(
284
+ "DECIMAL_AUTO_TRACE=%s set but no DECIMAL_API_KEY found, skipping",
285
+ auto_trace,
286
+ )
287
+ return
288
+
289
+ try:
290
+ init(
291
+ langchain=(auto_trace == "langchain"),
292
+ )
293
+ logger.info("DecimalAI auto-initialized via DECIMAL_AUTO_TRACE=%s", auto_trace)
294
+ except Exception:
295
+ logger.debug("Auto-init failed", exc_info=True)
296
+
297
+
298
+ _auto_init_from_env()