ol-llm 0.0.1__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.
- ol_llm-0.0.1/.gitignore +10 -0
- ol_llm-0.0.1/LICENSE +29 -0
- ol_llm-0.0.1/PKG-INFO +150 -0
- ol_llm-0.0.1/README.md +109 -0
- ol_llm-0.0.1/pyproject.toml +63 -0
- ol_llm-0.0.1/smoke/pyproject.toml +28 -0
- ol_llm-0.0.1/smoke/smoke_test.py +91 -0
- ol_llm-0.0.1/smoke/uv.lock +1400 -0
- ol_llm-0.0.1/src/ol_llm/__init__.py +94 -0
- ol_llm-0.0.1/src/ol_llm/attribution.py +116 -0
- ol_llm-0.0.1/src/ol_llm/errors.py +100 -0
- ol_llm-0.0.1/src/ol_llm/models.py +847 -0
- ol_llm-0.0.1/src/ol_llm/policy.py +230 -0
- ol_llm-0.0.1/src/ol_llm/py.typed +0 -0
- ol_llm-0.0.1/src/ol_llm/settings.py +134 -0
- ol_llm-0.0.1/src/ol_llm/testing.py +54 -0
- ol_llm-0.0.1/tests/conftest.py +33 -0
- ol_llm-0.0.1/tests/test_import.py +23 -0
- ol_llm-0.0.1/tests/test_models.py +608 -0
- ol_llm-0.0.1/tests/test_policy.py +120 -0
- ol_llm-0.0.1/tests/test_providers.py +314 -0
- ol_llm-0.0.1/tests/test_settings_and_attribution.py +117 -0
ol_llm-0.0.1/.gitignore
ADDED
ol_llm-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, MIT Open Learning
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
ol_llm-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ol-llm
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Shared library for LLM calls at MIT Open Learning, with Opik tracing, prompt management, and evaluation built in
|
|
5
|
+
Project-URL: Homepage, https://github.com/mitodl/ol-llm
|
|
6
|
+
Project-URL: Repository, https://github.com/mitodl/ol-llm
|
|
7
|
+
Project-URL: Issues, https://github.com/mitodl/ol-llm/issues
|
|
8
|
+
Author-email: MIT Open Learning <odl-devops@mit.edu>
|
|
9
|
+
License-Expression: BSD-3-Clause
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: llm,observability,opik,pydantic-ai
|
|
12
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: httpx>=0.28
|
|
21
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.38
|
|
22
|
+
Requires-Dist: opentelemetry-sdk>=1.38
|
|
23
|
+
Requires-Dist: opik==2.2.87
|
|
24
|
+
Provides-Extra: anthropic
|
|
25
|
+
Requires-Dist: pydantic-ai-slim[anthropic]<3,>=2.52; extra == 'anthropic'
|
|
26
|
+
Provides-Extra: bedrock
|
|
27
|
+
Requires-Dist: pydantic-ai-slim[bedrock]<3,>=2.52; extra == 'bedrock'
|
|
28
|
+
Provides-Extra: google
|
|
29
|
+
Requires-Dist: pydantic-ai-slim[google]<3,>=2.52; extra == 'google'
|
|
30
|
+
Provides-Extra: harness
|
|
31
|
+
Requires-Dist: pydantic-ai-harness<0.53,>=0.52; extra == 'harness'
|
|
32
|
+
Provides-Extra: memory
|
|
33
|
+
Requires-Dist: hindsight-client<0.11,>=0.10; extra == 'memory'
|
|
34
|
+
Provides-Extra: mistral
|
|
35
|
+
Requires-Dist: pydantic-ai-slim[mistral]<3,>=2.52; extra == 'mistral'
|
|
36
|
+
Provides-Extra: openai
|
|
37
|
+
Requires-Dist: pydantic-ai-slim[openai]<3,>=2.52; extra == 'openai'
|
|
38
|
+
Provides-Extra: pydantic-ai
|
|
39
|
+
Requires-Dist: pydantic-ai-slim<3,>=2.52; extra == 'pydantic-ai'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# ol-llm
|
|
43
|
+
|
|
44
|
+
The library every LLM call site at MIT Open Learning goes through. It owns the Opik
|
|
45
|
+
integration (tracing, prompt management, evaluation), a single timeout and retry policy, and
|
|
46
|
+
the attribution fields that let cost, quality, and traffic join across applications.
|
|
47
|
+
|
|
48
|
+
Importing `ol_llm` sets `OPIK_SENTRY_ENABLE=false` before anything imports the Opik SDK, which
|
|
49
|
+
otherwise replaces the host application's Sentry client with its own, so import it first.
|
|
50
|
+
|
|
51
|
+
The design is in [mitodl/ol-infrastructure `docs/plans/shared-llm-library-spec.md`](https://github.com/mitodl/ol-infrastructure/blob/main/docs/plans/shared-llm-library-spec.md).
|
|
52
|
+
The rules the code follows are in [`docs/`](https://github.com/mitodl/ol-llm/tree/main/docs):
|
|
53
|
+
the [error contract](https://github.com/mitodl/ol-llm/blob/main/docs/error-contract.md) and the
|
|
54
|
+
[attribution schema](https://github.com/mitodl/ol-llm/blob/main/docs/attribution.md).
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
Declare each call site once, as a module-level constant, and build its agent from it:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pydantic import BaseModel
|
|
62
|
+
|
|
63
|
+
import ol_llm
|
|
64
|
+
|
|
65
|
+
SUMMARY = ol_llm.UseCase(
|
|
66
|
+
app="mit-learn",
|
|
67
|
+
name="content-summary",
|
|
68
|
+
profile="batch",
|
|
69
|
+
model="openai:gpt-4o-mini",
|
|
70
|
+
fallback_models=("bedrock:global.anthropic.claude-haiku-4-5-20251001-v1:0",),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Summary(BaseModel):
|
|
75
|
+
text: str
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
summarizer = ol_llm.build_agent(SUMMARY, output_type=Summary, instructions="Summarize.")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The agent's model has a timeout on every request, retries rate limits and outages and
|
|
82
|
+
nothing else, falls back once the primary has spent its retries, and raises `LLMError`
|
|
83
|
+
subclasses (`LLMRateLimited`, `LLMUnavailable`, `LLMRejected`, ...) with the provider's
|
|
84
|
+
exception as the cause. Build agents once, not per request: each build creates new provider
|
|
85
|
+
clients.
|
|
86
|
+
|
|
87
|
+
Set `OL_LLM_ENVIRONMENT`, or call `ol_llm.configure(ol_llm.Settings(environment=...))` at
|
|
88
|
+
startup. Provider credentials are each SDK's own (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, the AWS
|
|
89
|
+
and Google default credential chains, and so on). Operators can change a use case's limits
|
|
90
|
+
without a release, e.g. `OL_LLM_CONTENT_SUMMARY_TIMEOUT_SECONDS=120`.
|
|
91
|
+
|
|
92
|
+
Call sites not built on PydanticAI (LangGraph, a plain SDK) apply the same numbers with
|
|
93
|
+
`ol_llm.resolve_policy(use_case)`, which needs no extra.
|
|
94
|
+
|
|
95
|
+
The entry points that wrap a run in an Opik trace (`run`, `run_stream`, `complete`) are not
|
|
96
|
+
released yet; until they are, `build_agent` gives the policy and errors without the tracing.
|
|
97
|
+
|
|
98
|
+
### Testing
|
|
99
|
+
|
|
100
|
+
`ol_llm.testing.override_models` makes `build_model` and `build_agent` use PydanticAI's
|
|
101
|
+
`TestModel` or `FunctionModel` in place of a provider, still under the library's retry and
|
|
102
|
+
error handling, and `tracing_disabled` turns Opik off. Script responses and failures with
|
|
103
|
+
`FunctionModel`; do not commit recorded provider responses.
|
|
104
|
+
|
|
105
|
+
## Extras
|
|
106
|
+
|
|
107
|
+
The base install is the Opik SDK and OpenTelemetry, which every call site needs. Everything
|
|
108
|
+
else is opt-in:
|
|
109
|
+
|
|
110
|
+
| Extra | Installs | For |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `pydantic-ai` | `pydantic-ai-slim` | The default construction path for agents and single calls |
|
|
113
|
+
| `openai`, `anthropic`, `bedrock`, `google`, `mistral` | the matching `pydantic-ai-slim` extra | A provider SDK; each also installs PydanticAI |
|
|
114
|
+
| `harness` | `pydantic-ai-harness` | In-session memory, compaction, and conversation search. Each harness release pins PydanticAI exactly |
|
|
115
|
+
| `memory` | `hindsight-client` | The shared memory service's client. Provisional: the backend is still being evaluated |
|
|
116
|
+
|
|
117
|
+
For example, `ol-llm[bedrock,harness]`.
|
|
118
|
+
|
|
119
|
+
## Installing without the Opik SDK's heavy dependencies
|
|
120
|
+
|
|
121
|
+
The `opik` package hard-requires litellm, openai, pytest, and several other packages the
|
|
122
|
+
tracing and prompt paths never import. uv consumers can drop them:
|
|
123
|
+
|
|
124
|
+
```toml
|
|
125
|
+
[tool.uv]
|
|
126
|
+
override-dependencies = [
|
|
127
|
+
"boto3-stubs; sys_platform == 'never'",
|
|
128
|
+
"click; sys_platform == 'never'",
|
|
129
|
+
"litellm; sys_platform == 'never'",
|
|
130
|
+
"openai; sys_platform == 'never'",
|
|
131
|
+
"pytest; sys_platform == 'never'",
|
|
132
|
+
"rapidfuzz; sys_platform == 'never'",
|
|
133
|
+
"tree-sitter; sys_platform == 'never'",
|
|
134
|
+
"tree-sitter-javascript; sys_platform == 'never'",
|
|
135
|
+
"tree-sitter-typescript; sys_platform == 'never'",
|
|
136
|
+
"watchfiles; sys_platform == 'never'",
|
|
137
|
+
]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Drop `openai` from that list if the application uses the `openai` extra. Opik's evaluation
|
|
141
|
+
metrics need litellm, so the overrides belong in runtime images, not in an environment that
|
|
142
|
+
runs evaluations. CI checks this exact list on every change (`python/smoke/`).
|
|
143
|
+
|
|
144
|
+
Applications that cap how new a dependency may be (`exclude-newer` in uv) should exempt this
|
|
145
|
+
package so releases are usable the day they ship:
|
|
146
|
+
|
|
147
|
+
```toml
|
|
148
|
+
[tool.uv.exclude-newer-package]
|
|
149
|
+
ol-llm = "0d"
|
|
150
|
+
```
|
ol_llm-0.0.1/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# ol-llm
|
|
2
|
+
|
|
3
|
+
The library every LLM call site at MIT Open Learning goes through. It owns the Opik
|
|
4
|
+
integration (tracing, prompt management, evaluation), a single timeout and retry policy, and
|
|
5
|
+
the attribution fields that let cost, quality, and traffic join across applications.
|
|
6
|
+
|
|
7
|
+
Importing `ol_llm` sets `OPIK_SENTRY_ENABLE=false` before anything imports the Opik SDK, which
|
|
8
|
+
otherwise replaces the host application's Sentry client with its own, so import it first.
|
|
9
|
+
|
|
10
|
+
The design is in [mitodl/ol-infrastructure `docs/plans/shared-llm-library-spec.md`](https://github.com/mitodl/ol-infrastructure/blob/main/docs/plans/shared-llm-library-spec.md).
|
|
11
|
+
The rules the code follows are in [`docs/`](https://github.com/mitodl/ol-llm/tree/main/docs):
|
|
12
|
+
the [error contract](https://github.com/mitodl/ol-llm/blob/main/docs/error-contract.md) and the
|
|
13
|
+
[attribution schema](https://github.com/mitodl/ol-llm/blob/main/docs/attribution.md).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Declare each call site once, as a module-level constant, and build its agent from it:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from pydantic import BaseModel
|
|
21
|
+
|
|
22
|
+
import ol_llm
|
|
23
|
+
|
|
24
|
+
SUMMARY = ol_llm.UseCase(
|
|
25
|
+
app="mit-learn",
|
|
26
|
+
name="content-summary",
|
|
27
|
+
profile="batch",
|
|
28
|
+
model="openai:gpt-4o-mini",
|
|
29
|
+
fallback_models=("bedrock:global.anthropic.claude-haiku-4-5-20251001-v1:0",),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Summary(BaseModel):
|
|
34
|
+
text: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
summarizer = ol_llm.build_agent(SUMMARY, output_type=Summary, instructions="Summarize.")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The agent's model has a timeout on every request, retries rate limits and outages and
|
|
41
|
+
nothing else, falls back once the primary has spent its retries, and raises `LLMError`
|
|
42
|
+
subclasses (`LLMRateLimited`, `LLMUnavailable`, `LLMRejected`, ...) with the provider's
|
|
43
|
+
exception as the cause. Build agents once, not per request: each build creates new provider
|
|
44
|
+
clients.
|
|
45
|
+
|
|
46
|
+
Set `OL_LLM_ENVIRONMENT`, or call `ol_llm.configure(ol_llm.Settings(environment=...))` at
|
|
47
|
+
startup. Provider credentials are each SDK's own (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, the AWS
|
|
48
|
+
and Google default credential chains, and so on). Operators can change a use case's limits
|
|
49
|
+
without a release, e.g. `OL_LLM_CONTENT_SUMMARY_TIMEOUT_SECONDS=120`.
|
|
50
|
+
|
|
51
|
+
Call sites not built on PydanticAI (LangGraph, a plain SDK) apply the same numbers with
|
|
52
|
+
`ol_llm.resolve_policy(use_case)`, which needs no extra.
|
|
53
|
+
|
|
54
|
+
The entry points that wrap a run in an Opik trace (`run`, `run_stream`, `complete`) are not
|
|
55
|
+
released yet; until they are, `build_agent` gives the policy and errors without the tracing.
|
|
56
|
+
|
|
57
|
+
### Testing
|
|
58
|
+
|
|
59
|
+
`ol_llm.testing.override_models` makes `build_model` and `build_agent` use PydanticAI's
|
|
60
|
+
`TestModel` or `FunctionModel` in place of a provider, still under the library's retry and
|
|
61
|
+
error handling, and `tracing_disabled` turns Opik off. Script responses and failures with
|
|
62
|
+
`FunctionModel`; do not commit recorded provider responses.
|
|
63
|
+
|
|
64
|
+
## Extras
|
|
65
|
+
|
|
66
|
+
The base install is the Opik SDK and OpenTelemetry, which every call site needs. Everything
|
|
67
|
+
else is opt-in:
|
|
68
|
+
|
|
69
|
+
| Extra | Installs | For |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `pydantic-ai` | `pydantic-ai-slim` | The default construction path for agents and single calls |
|
|
72
|
+
| `openai`, `anthropic`, `bedrock`, `google`, `mistral` | the matching `pydantic-ai-slim` extra | A provider SDK; each also installs PydanticAI |
|
|
73
|
+
| `harness` | `pydantic-ai-harness` | In-session memory, compaction, and conversation search. Each harness release pins PydanticAI exactly |
|
|
74
|
+
| `memory` | `hindsight-client` | The shared memory service's client. Provisional: the backend is still being evaluated |
|
|
75
|
+
|
|
76
|
+
For example, `ol-llm[bedrock,harness]`.
|
|
77
|
+
|
|
78
|
+
## Installing without the Opik SDK's heavy dependencies
|
|
79
|
+
|
|
80
|
+
The `opik` package hard-requires litellm, openai, pytest, and several other packages the
|
|
81
|
+
tracing and prompt paths never import. uv consumers can drop them:
|
|
82
|
+
|
|
83
|
+
```toml
|
|
84
|
+
[tool.uv]
|
|
85
|
+
override-dependencies = [
|
|
86
|
+
"boto3-stubs; sys_platform == 'never'",
|
|
87
|
+
"click; sys_platform == 'never'",
|
|
88
|
+
"litellm; sys_platform == 'never'",
|
|
89
|
+
"openai; sys_platform == 'never'",
|
|
90
|
+
"pytest; sys_platform == 'never'",
|
|
91
|
+
"rapidfuzz; sys_platform == 'never'",
|
|
92
|
+
"tree-sitter; sys_platform == 'never'",
|
|
93
|
+
"tree-sitter-javascript; sys_platform == 'never'",
|
|
94
|
+
"tree-sitter-typescript; sys_platform == 'never'",
|
|
95
|
+
"watchfiles; sys_platform == 'never'",
|
|
96
|
+
]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Drop `openai` from that list if the application uses the `openai` extra. Opik's evaluation
|
|
100
|
+
metrics need litellm, so the overrides belong in runtime images, not in an environment that
|
|
101
|
+
runs evaluations. CI checks this exact list on every change (`python/smoke/`).
|
|
102
|
+
|
|
103
|
+
Applications that cap how new a dependency may be (`exclude-newer` in uv) should exempt this
|
|
104
|
+
package so releases are usable the day they ship:
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
[tool.uv.exclude-newer-package]
|
|
108
|
+
ol-llm = "0d"
|
|
109
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ol-llm"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Shared library for LLM calls at MIT Open Learning, with Opik tracing, prompt management, and evaluation built in"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "BSD-3-Clause"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "MIT Open Learning", email = "odl-devops@mit.edu" }]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
keywords = ["llm", "opik", "pydantic-ai", "observability"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"Programming Language :: Python :: 3.14",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
# The base install is what every call site needs whatever it is built on: the
|
|
21
|
+
# Opik SDK for prompts and the root trace, and OpenTelemetry for the spans a
|
|
22
|
+
# framework emits. Everything framework- or backend-specific is an extra.
|
|
23
|
+
#
|
|
24
|
+
# opik is pinned exactly: consumers slim it with uv override-dependencies, and a
|
|
25
|
+
# release that starts importing a dropped package eagerly would break them at
|
|
26
|
+
# import time. The smoke project in python/smoke/ catches that on every bump.
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.28",
|
|
29
|
+
"opentelemetry-exporter-otlp-proto-http>=1.38",
|
|
30
|
+
"opentelemetry-sdk>=1.38",
|
|
31
|
+
"opik==2.2.87",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
# The default construction path for agents and single calls.
|
|
36
|
+
pydantic-ai = ["pydantic-ai-slim>=2.52,<3"]
|
|
37
|
+
# Provider SDKs, each through PydanticAI's own extra, so a provider extra also
|
|
38
|
+
# installs PydanticAI.
|
|
39
|
+
anthropic = ["pydantic-ai-slim[anthropic]>=2.52,<3"]
|
|
40
|
+
bedrock = ["pydantic-ai-slim[bedrock]>=2.52,<3"]
|
|
41
|
+
google = ["pydantic-ai-slim[google]>=2.52,<3"]
|
|
42
|
+
mistral = ["pydantic-ai-slim[mistral]>=2.52,<3"]
|
|
43
|
+
openai = ["pydantic-ai-slim[openai]>=2.52,<3"]
|
|
44
|
+
# PydanticAI's first-party capabilities: in-session memory, compaction, and
|
|
45
|
+
# conversation search. Each harness release pins pydantic-ai-slim exactly, so
|
|
46
|
+
# this extra also fixes the PydanticAI version. 0.x: pinned to a minor series.
|
|
47
|
+
harness = ["pydantic-ai-harness>=0.52,<0.53"]
|
|
48
|
+
# Client for the shared memory service (cross-session recall and user-scoped
|
|
49
|
+
# facts). Hindsight is the leading candidate and its PoC is still open, so this
|
|
50
|
+
# extra may change backend before the memory interface ships.
|
|
51
|
+
memory = ["hindsight-client>=0.10,<0.11"]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://github.com/mitodl/ol-llm"
|
|
55
|
+
Repository = "https://github.com/mitodl/ol-llm"
|
|
56
|
+
Issues = "https://github.com/mitodl/ol-llm/issues"
|
|
57
|
+
|
|
58
|
+
[build-system]
|
|
59
|
+
requires = ["hatchling"]
|
|
60
|
+
build-backend = "hatchling.build"
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.wheel]
|
|
63
|
+
packages = ["src/ol_llm"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# A consumer-shaped project: ol-llm installed with the overrides a uv consumer
|
|
2
|
+
# copies to drop the opik SDK's heavy dependencies. CI runs smoke_test.py in this
|
|
3
|
+
# environment on every change, so an opik release that starts importing a dropped
|
|
4
|
+
# package eagerly fails here before any app picks it up.
|
|
5
|
+
[project]
|
|
6
|
+
name = "ol-llm-smoke"
|
|
7
|
+
version = "0"
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
# The pydantic-ai extra, because the smoke test runs an instrumented agent.
|
|
10
|
+
dependencies = ["ol-llm[pydantic-ai]"]
|
|
11
|
+
|
|
12
|
+
[tool.uv]
|
|
13
|
+
package = false
|
|
14
|
+
override-dependencies = [
|
|
15
|
+
"boto3-stubs; sys_platform == 'never'",
|
|
16
|
+
"click; sys_platform == 'never'",
|
|
17
|
+
"litellm; sys_platform == 'never'",
|
|
18
|
+
"openai; sys_platform == 'never'",
|
|
19
|
+
"pytest; sys_platform == 'never'",
|
|
20
|
+
"rapidfuzz; sys_platform == 'never'",
|
|
21
|
+
"tree-sitter; sys_platform == 'never'",
|
|
22
|
+
"tree-sitter-javascript; sys_platform == 'never'",
|
|
23
|
+
"tree-sitter-typescript; sys_platform == 'never'",
|
|
24
|
+
"watchfiles; sys_platform == 'never'",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[tool.uv.sources]
|
|
28
|
+
ol-llm = { path = "..", editable = true }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Import-and-trace check for ol-llm installed with the opik SDK slimmed.
|
|
2
|
+
|
|
3
|
+
Runs as a plain script, not under pytest: pytest is one of the packages the
|
|
4
|
+
overrides drop. Exits non-zero on any failure.
|
|
5
|
+
|
|
6
|
+
What it proves, in an environment shaped like a consumer's runtime image:
|
|
7
|
+
- ol_llm and opik import with litellm, openai, pytest, and tree-sitter absent;
|
|
8
|
+
- an @opik.track root with an instrumented agent from ol_llm.build_agent inside it
|
|
9
|
+
produces OTel spans that OpikSpanProcessor attaches to the tracked trace;
|
|
10
|
+
- importing ol_llm first leaves no Sentry client running.
|
|
11
|
+
|
|
12
|
+
No Opik server is needed. The SDK is pointed at a closed port, so its background
|
|
13
|
+
sender fails quietly; the assertions read the spans from an in-memory exporter.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
os.environ["OPIK_URL_OVERRIDE"] = "http://127.0.0.1:9/api"
|
|
20
|
+
os.environ["OPIK_WORKSPACE"] = "default"
|
|
21
|
+
|
|
22
|
+
# ol_llm before opik, so OPIK_SENTRY_ENABLE is set before opik reads it.
|
|
23
|
+
import ol_llm
|
|
24
|
+
import opik
|
|
25
|
+
import sentry_sdk
|
|
26
|
+
from ol_llm.models import build_agent, override_models
|
|
27
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
28
|
+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
29
|
+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
|
|
30
|
+
InMemorySpanExporter,
|
|
31
|
+
)
|
|
32
|
+
from opik.integrations.otel import OpikSpanProcessor
|
|
33
|
+
from pydantic_ai.capabilities import Instrumentation
|
|
34
|
+
from pydantic_ai.models.instrumented import InstrumentationSettings
|
|
35
|
+
from pydantic_ai.models.test import TestModel
|
|
36
|
+
|
|
37
|
+
DROPPED = ("litellm", "openai", "pytest", "tree_sitter", "click")
|
|
38
|
+
OPIK_TRACE_ID = "opik.trace_id"
|
|
39
|
+
SMOKE = ol_llm.UseCase(
|
|
40
|
+
app="ol-llm-smoke", name="smoke", profile="request", model="openai:smoke"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main() -> int:
|
|
45
|
+
"""Run the checks and return a process exit status."""
|
|
46
|
+
exporter = InMemorySpanExporter()
|
|
47
|
+
provider = TracerProvider()
|
|
48
|
+
provider.add_span_processor(OpikSpanProcessor())
|
|
49
|
+
provider.add_span_processor(SimpleSpanProcessor(exporter))
|
|
50
|
+
|
|
51
|
+
ol_llm.configure(ol_llm.Settings(environment="ci"))
|
|
52
|
+
# The test model stands in for the provider; the policy wrapper, the agent's
|
|
53
|
+
# capabilities, and the import graph are the library's own.
|
|
54
|
+
with override_models(TestModel()):
|
|
55
|
+
agent = build_agent(
|
|
56
|
+
SMOKE,
|
|
57
|
+
capabilities=[
|
|
58
|
+
Instrumentation(
|
|
59
|
+
settings=InstrumentationSettings(tracer_provider=provider)
|
|
60
|
+
)
|
|
61
|
+
],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@opik.track(name="smoke", project_name="ol-llm-smoke")
|
|
65
|
+
def run() -> str:
|
|
66
|
+
return agent.run_sync("ping").output
|
|
67
|
+
|
|
68
|
+
output = run()
|
|
69
|
+
spans = exporter.get_finished_spans()
|
|
70
|
+
attached = [s for s in spans if (s.attributes or {}).get(OPIK_TRACE_ID)]
|
|
71
|
+
imported = [name for name in DROPPED if name in sys.modules]
|
|
72
|
+
failures = []
|
|
73
|
+
if not spans:
|
|
74
|
+
failures.append("the agent emitted no OTel spans")
|
|
75
|
+
if len(attached) != len(spans):
|
|
76
|
+
failures.append(
|
|
77
|
+
f"{len(spans) - len(attached)} of {len(spans)} spans lack {OPIK_TRACE_ID}"
|
|
78
|
+
)
|
|
79
|
+
if imported:
|
|
80
|
+
failures.append(f"dropped packages were imported: {imported}")
|
|
81
|
+
if sentry_sdk.get_client().dsn is not None:
|
|
82
|
+
failures.append("a Sentry client is running after importing opik")
|
|
83
|
+
|
|
84
|
+
print(f"ol-llm {ol_llm.__version__}: output={output!r} spans={len(spans)}")
|
|
85
|
+
for failure in failures:
|
|
86
|
+
print(f"FAIL: {failure}")
|
|
87
|
+
return 1 if failures else 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
sys.exit(main())
|