sofias-sdk-lite 0.1.5__tar.gz → 0.2.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.
- sofias_sdk_lite-0.2.0/LICENSE +69 -0
- sofias_sdk_lite-0.1.5/README.md → sofias_sdk_lite-0.2.0/PKG-INFO +57 -10
- sofias_sdk_lite-0.1.5/PKG-INFO → sofias_sdk_lite-0.2.0/README.md +21 -28
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/pyproject.toml +32 -2
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/pyproject.toml.orig +24 -2
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/__init__.py +2 -2
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/agent_settings.py +2 -2
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/__init__.py +11 -11
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/factory.py +47 -34
- sofias_sdk_lite-0.1.5/src/sofias_sdk_lite/llm/bifrost.py → sofias_sdk_lite-0.2.0/src/sofias_sdk_lite/llm/gateway.py +53 -36
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/openai_compatible.py +2 -2
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/protocol.py +1 -1
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/__init__.py +1 -1
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/tracing.py +1 -1
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/agent.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/agent_builder.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/agent_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/execution_context.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/middleware.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/response_workflow.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/streaming.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/defaults.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/runtime_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/sdk_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/source.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/agent_contracts.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/base_contracts.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/node_contracts.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/strict_contract.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/errors/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/errors/circuit_breaker.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/errors/error_handler.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/errors/exceptions.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/tokens.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/tools.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/messaging/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/messaging/models.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/aggregator_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/aggregator_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/base_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_transport.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/function_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/llm_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/llm_node_config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/plan_executor.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/planner_node.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/planning_models.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/prompt_assembler.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/_log.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/events.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/trace_context.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/py.typed +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/client.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/consumer.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/delegation_transport.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/publisher.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/rpc_client.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/types.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/routing/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/routing/router.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/routing/strategies.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/runner/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/runner/config.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/runner/runner.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/state/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/state/conversation_state.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/state/history.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/state/memory.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/tools.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/workflows/__init__.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/workflows/chat.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/workflows/null.py +0 -0
- {sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/workflows/streaming_chat.py +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Sofias SDK Lite License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sofias Tech, S.L. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This licence applies to the software distributed as "sofias-sdk-lite" (the
|
|
6
|
+
"SDK"), including its source code, documentation and examples. By installing
|
|
7
|
+
or using the SDK you accept it. If you do not accept it, do not use the SDK.
|
|
8
|
+
|
|
9
|
+
1. Permitted use
|
|
10
|
+
|
|
11
|
+
Sofias Tech, S.L. ("Sofias") grants you a free, non-exclusive,
|
|
12
|
+
non-transferable and revocable licence to download, install and use the SDK
|
|
13
|
+
solely to develop, test and run agents intended to run on the Sofias
|
|
14
|
+
platform, including its developer portal and marketplace.
|
|
15
|
+
|
|
16
|
+
2. Source availability
|
|
17
|
+
|
|
18
|
+
The source code is made available so that you can read it and understand
|
|
19
|
+
how the SDK works. Access to the source code does not grant any right
|
|
20
|
+
beyond those set out in section 1.
|
|
21
|
+
|
|
22
|
+
3. Restrictions
|
|
23
|
+
|
|
24
|
+
Except as expressly permitted in section 1, you may not:
|
|
25
|
+
|
|
26
|
+
a) use the SDK, or any part or derivative of it, to build, run or offer
|
|
27
|
+
agents, applications or services for any platform other than Sofias;
|
|
28
|
+
b) copy, modify, adapt, translate or create derivative works of the SDK,
|
|
29
|
+
other than the local copies needed for the permitted use;
|
|
30
|
+
c) distribute, publish, sublicense, sell, rent or otherwise make the SDK or
|
|
31
|
+
any derivative of it available to third parties;
|
|
32
|
+
d) remove or alter this notice or any other notice of ownership.
|
|
33
|
+
|
|
34
|
+
4. Your agents
|
|
35
|
+
|
|
36
|
+
The agents you build with the SDK are yours. This licence does not give
|
|
37
|
+
Sofias any right over them. Any right Sofias needs to host, run or offer
|
|
38
|
+
your agents is governed by the terms you accept when you publish on the
|
|
39
|
+
Sofias platform.
|
|
40
|
+
|
|
41
|
+
5. Termination
|
|
42
|
+
|
|
43
|
+
This licence ends automatically if you breach any of its terms. Sofias may
|
|
44
|
+
also revoke it by written notice. On termination you must stop using the SDK
|
|
45
|
+
and delete every copy in your possession.
|
|
46
|
+
|
|
47
|
+
6. No warranty
|
|
48
|
+
|
|
49
|
+
THE SDK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
50
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION THE WARRANTIES OF MERCHANTABILITY,
|
|
51
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
|
52
|
+
|
|
53
|
+
7. Limitation of liability
|
|
54
|
+
|
|
55
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, SOFIAS SHALL NOT BE LIABLE FOR ANY
|
|
56
|
+
DAMAGE ARISING FROM THE USE OF, OR THE INABILITY TO USE, THE SDK.
|
|
57
|
+
|
|
58
|
+
8. Changes
|
|
59
|
+
|
|
60
|
+
Sofias may publish new versions of this licence. Each release of the SDK is
|
|
61
|
+
governed by the licence included with it.
|
|
62
|
+
|
|
63
|
+
9. Governing law and jurisdiction
|
|
64
|
+
|
|
65
|
+
This licence is governed by Spanish law. Any dispute shall be submitted to
|
|
66
|
+
the courts of Donostia - San Sebastian (Gipuzkoa), Spain, unless mandatory
|
|
67
|
+
law provides otherwise.
|
|
68
|
+
|
|
69
|
+
Contact: support@sofias.ai
|
|
@@ -1,7 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sofias-sdk-lite
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Build agents on the Sofias agent graph and run them over RabbitMQ.
|
|
5
|
+
Keywords: agents,ai,llm,rabbitmq,sofias
|
|
6
|
+
Author: Sofias Tech, S.L.
|
|
7
|
+
Author-email: Sofias Tech, S.L. <support@sofias.ai>
|
|
8
|
+
License-Expression: LicenseRef-Sofias-SDK-Lite
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
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: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
20
|
+
Requires-Dist: aio-pika>=9.0.0
|
|
21
|
+
Requires-Dist: rstream>=1.0.0
|
|
22
|
+
Requires-Dist: nh3>=0.3.6
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: mkdocs>=1.6,<2 ; extra == 'docs'
|
|
25
|
+
Requires-Dist: mkdocs-material>=9.5 ; extra == 'docs'
|
|
26
|
+
Requires-Dist: mkdocstrings[python]>=0.25 ; extra == 'docs'
|
|
27
|
+
Requires-Dist: opentelemetry-api>=1.20.0 ; extra == 'otel'
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Project-URL: Homepage, https://sofias.ai/
|
|
30
|
+
Project-URL: Documentation, https://docs.sofias.ai/
|
|
31
|
+
Project-URL: Repository, https://github.com/Sofias-ai/sofias-sdk-lite
|
|
32
|
+
Project-URL: Changelog, https://github.com/Sofias-ai/sofias-sdk-lite/blob/main/CHANGELOG.md
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Provides-Extra: otel
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
1
37
|
# sofias-sdk-lite
|
|
2
38
|
|
|
3
39
|
Build multi-node agents on a declarative graph and run them against RabbitMQ.
|
|
4
40
|
|
|
41
|
+
**Documentation:** [docs.sofias.ai](https://docs.sofias.ai/) · **Developer portal:** [developers.sofias.ai](https://developers.sofias.ai/) · **Source:** [github.com/Sofias-ai/sofias-sdk-lite](https://github.com/Sofias-ai/sofias-sdk-lite)
|
|
42
|
+
|
|
5
43
|
`sofias-sdk-lite` gives you:
|
|
6
44
|
|
|
7
45
|
- **An agent graph** — LLM nodes, function nodes, delegation nodes (agent-to-agent),
|
|
@@ -13,8 +51,8 @@ Build multi-node agents on a declarative graph and run them against RabbitMQ.
|
|
|
13
51
|
- **A runner** — `AgentRunner` consumes tasks from a queue, resolves per-request
|
|
14
52
|
settings, executes your agent, and streams the response back — with graceful
|
|
15
53
|
shutdown, retries, and circuit breakers built in.
|
|
16
|
-
- **An LLM that is already wired** — LLM nodes talk to the Sofias
|
|
17
|
-
|
|
54
|
+
- **An LLM that is already wired** — LLM nodes talk to the Sofias gateway
|
|
55
|
+
(or any OpenAI-compatible endpoint) using the model, URL and key
|
|
18
56
|
from the agent's settings or the `SOFIAS_LLM_*` environment. `create_llm()`
|
|
19
57
|
builds the client when you need it explicitly; `LLMCallable` stays a protocol
|
|
20
58
|
for anything the bundled clients do not cover.
|
|
@@ -97,12 +135,11 @@ resolved at `build()`:
|
|
|
97
135
|
|
|
98
136
|
1. Under `AgentRunner`, from the per-task settings the platform sends
|
|
99
137
|
(`model_name`, `router_url`, `router_api_key` on `BaseAgentSettings`).
|
|
100
|
-
2. Otherwise from the environment: `SOFIAS_LLM_MODEL`, `SOFIAS_LLM_BASE_URL
|
|
101
|
-
(default `
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
portal-deployed agent needs no LLM configuration at all.
|
|
138
|
+
2. Otherwise from the environment: `SOFIAS_LLM_MODEL`, `SOFIAS_LLM_BASE_URL`,
|
|
139
|
+
`SOFIAS_LLM_API_KEY` and `SOFIAS_LLM_PROVIDER` (default `sofias`). The
|
|
140
|
+
Sofias platform injects them into every agent it runs, so an agent
|
|
141
|
+
deployed through the developer portal needs no LLM configuration at all.
|
|
142
|
+
There is no default URL: to run locally against a real gateway, set it.
|
|
106
143
|
3. Or explicitly: `.with_llm(create_llm(model="default", api_key=...))`.
|
|
107
144
|
|
|
108
145
|
```python
|
|
@@ -173,11 +210,21 @@ if __name__ == "__main__":
|
|
|
173
210
|
## Scope (v1)
|
|
174
211
|
|
|
175
212
|
Core agent graph, RabbitMQ messaging, the runner, and LLM clients for the
|
|
176
|
-
|
|
213
|
+
Sofias gateway / any OpenAI-compatible endpoint ship today. Memory and MCP
|
|
177
214
|
tool discovery are intentionally out of scope for v1 — the SDK ships the
|
|
178
215
|
relevant protocols (`MemoryProvider`, `ToolProvider`) so you can plug in
|
|
179
216
|
your own, and these become optional extras in a later release.
|
|
180
217
|
|
|
218
|
+
## Source and contributions
|
|
219
|
+
|
|
220
|
+
The public repository holds read-only release snapshots: each release of
|
|
221
|
+
`sofias-sdk-lite` appears there as a single commit. Development happens
|
|
222
|
+
elsewhere, so pull requests cannot be merged and we do not accept
|
|
223
|
+
contributions at the moment. For questions or to report a problem, write to
|
|
224
|
+
support@sofias.ai.
|
|
225
|
+
|
|
181
226
|
## License
|
|
182
227
|
|
|
183
|
-
|
|
228
|
+
Source-available under the [Sofias SDK Lite License](https://github.com/Sofias-ai/sofias-sdk-lite/blob/main/LICENSE):
|
|
229
|
+
you may use `sofias-sdk-lite` to develop, test and run agents for the Sofias
|
|
230
|
+
platform. The agents you build with it are yours.
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: sofias-sdk-lite
|
|
3
|
-
Version: 0.1.5
|
|
4
|
-
Summary: Build agents on the Sofias agent graph and run them over RabbitMQ.
|
|
5
|
-
License-Expression: Apache-2.0
|
|
6
|
-
Requires-Dist: pydantic>=2.0
|
|
7
|
-
Requires-Dist: aio-pika>=9.0.0
|
|
8
|
-
Requires-Dist: rstream>=1.0.0
|
|
9
|
-
Requires-Dist: nh3>=0.3.6
|
|
10
|
-
Requires-Dist: httpx>=0.27.0
|
|
11
|
-
Requires-Dist: mkdocs-material>=9.5 ; extra == 'docs'
|
|
12
|
-
Requires-Dist: mkdocstrings[python]>=0.25 ; extra == 'docs'
|
|
13
|
-
Requires-Dist: opentelemetry-api>=1.20.0 ; extra == 'otel'
|
|
14
|
-
Requires-Python: >=3.11
|
|
15
|
-
Provides-Extra: docs
|
|
16
|
-
Provides-Extra: otel
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
1
|
# sofias-sdk-lite
|
|
20
2
|
|
|
21
3
|
Build multi-node agents on a declarative graph and run them against RabbitMQ.
|
|
22
4
|
|
|
5
|
+
**Documentation:** [docs.sofias.ai](https://docs.sofias.ai/) · **Developer portal:** [developers.sofias.ai](https://developers.sofias.ai/) · **Source:** [github.com/Sofias-ai/sofias-sdk-lite](https://github.com/Sofias-ai/sofias-sdk-lite)
|
|
6
|
+
|
|
23
7
|
`sofias-sdk-lite` gives you:
|
|
24
8
|
|
|
25
9
|
- **An agent graph** — LLM nodes, function nodes, delegation nodes (agent-to-agent),
|
|
@@ -31,8 +15,8 @@ Build multi-node agents on a declarative graph and run them against RabbitMQ.
|
|
|
31
15
|
- **A runner** — `AgentRunner` consumes tasks from a queue, resolves per-request
|
|
32
16
|
settings, executes your agent, and streams the response back — with graceful
|
|
33
17
|
shutdown, retries, and circuit breakers built in.
|
|
34
|
-
- **An LLM that is already wired** — LLM nodes talk to the Sofias
|
|
35
|
-
|
|
18
|
+
- **An LLM that is already wired** — LLM nodes talk to the Sofias gateway
|
|
19
|
+
(or any OpenAI-compatible endpoint) using the model, URL and key
|
|
36
20
|
from the agent's settings or the `SOFIAS_LLM_*` environment. `create_llm()`
|
|
37
21
|
builds the client when you need it explicitly; `LLMCallable` stays a protocol
|
|
38
22
|
for anything the bundled clients do not cover.
|
|
@@ -115,12 +99,11 @@ resolved at `build()`:
|
|
|
115
99
|
|
|
116
100
|
1. Under `AgentRunner`, from the per-task settings the platform sends
|
|
117
101
|
(`model_name`, `router_url`, `router_api_key` on `BaseAgentSettings`).
|
|
118
|
-
2. Otherwise from the environment: `SOFIAS_LLM_MODEL`, `SOFIAS_LLM_BASE_URL
|
|
119
|
-
(default `
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
portal-deployed agent needs no LLM configuration at all.
|
|
102
|
+
2. Otherwise from the environment: `SOFIAS_LLM_MODEL`, `SOFIAS_LLM_BASE_URL`,
|
|
103
|
+
`SOFIAS_LLM_API_KEY` and `SOFIAS_LLM_PROVIDER` (default `sofias`). The
|
|
104
|
+
Sofias platform injects them into every agent it runs, so an agent
|
|
105
|
+
deployed through the developer portal needs no LLM configuration at all.
|
|
106
|
+
There is no default URL: to run locally against a real gateway, set it.
|
|
124
107
|
3. Or explicitly: `.with_llm(create_llm(model="default", api_key=...))`.
|
|
125
108
|
|
|
126
109
|
```python
|
|
@@ -191,11 +174,21 @@ if __name__ == "__main__":
|
|
|
191
174
|
## Scope (v1)
|
|
192
175
|
|
|
193
176
|
Core agent graph, RabbitMQ messaging, the runner, and LLM clients for the
|
|
194
|
-
|
|
177
|
+
Sofias gateway / any OpenAI-compatible endpoint ship today. Memory and MCP
|
|
195
178
|
tool discovery are intentionally out of scope for v1 — the SDK ships the
|
|
196
179
|
relevant protocols (`MemoryProvider`, `ToolProvider`) so you can plug in
|
|
197
180
|
your own, and these become optional extras in a later release.
|
|
198
181
|
|
|
182
|
+
## Source and contributions
|
|
183
|
+
|
|
184
|
+
The public repository holds read-only release snapshots: each release of
|
|
185
|
+
`sofias-sdk-lite` appears there as a single commit. Development happens
|
|
186
|
+
elsewhere, so pull requests cannot be merged and we do not accept
|
|
187
|
+
contributions at the moment. For questions or to report a problem, write to
|
|
188
|
+
support@sofias.ai.
|
|
189
|
+
|
|
199
190
|
## License
|
|
200
191
|
|
|
201
|
-
|
|
192
|
+
Source-available under the [Sofias SDK Lite License](https://github.com/Sofias-ai/sofias-sdk-lite/blob/main/LICENSE):
|
|
193
|
+
you may use `sofias-sdk-lite` to develop, test and run agents for the Sofias
|
|
194
|
+
platform. The agents you build with it are yours.
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sofias-sdk-lite"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Build agents on the Sofias agent graph and run them over RabbitMQ."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
|
-
license = "
|
|
7
|
+
license = "LicenseRef-Sofias-SDK-Lite"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = [
|
|
10
|
+
"agents",
|
|
11
|
+
"ai",
|
|
12
|
+
"llm",
|
|
13
|
+
"rabbitmq",
|
|
14
|
+
"sofias",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
8
27
|
dependencies = [
|
|
9
28
|
"pydantic>=2.0",
|
|
10
29
|
"aio-pika>=9.0.0",
|
|
@@ -13,9 +32,20 @@ dependencies = [
|
|
|
13
32
|
"httpx>=0.27.0",
|
|
14
33
|
]
|
|
15
34
|
|
|
35
|
+
[[project.authors]]
|
|
36
|
+
name = "Sofias Tech, S.L."
|
|
37
|
+
email = "support@sofias.ai"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://sofias.ai/"
|
|
41
|
+
Documentation = "https://docs.sofias.ai/"
|
|
42
|
+
Repository = "https://github.com/Sofias-ai/sofias-sdk-lite"
|
|
43
|
+
Changelog = "https://github.com/Sofias-ai/sofias-sdk-lite/blob/main/CHANGELOG.md"
|
|
44
|
+
|
|
16
45
|
[project.optional-dependencies]
|
|
17
46
|
otel = ["opentelemetry-api>=1.20.0"]
|
|
18
47
|
docs = [
|
|
48
|
+
"mkdocs>=1.6,<2",
|
|
19
49
|
"mkdocs-material>=9.5",
|
|
20
50
|
"mkdocstrings[python]>=0.25",
|
|
21
51
|
]
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sofias-sdk-lite"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Build agents on the Sofias agent graph and run them over RabbitMQ."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
|
-
license = "
|
|
7
|
+
license = "LicenseRef-Sofias-SDK-Lite"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Sofias Tech, S.L.", email = "support@sofias.ai" }]
|
|
10
|
+
keywords = ["agents", "ai", "llm", "rabbitmq", "sofias"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
8
22
|
dependencies = [
|
|
9
23
|
"pydantic>=2.0",
|
|
10
24
|
"aio-pika>=9.0.0",
|
|
@@ -13,11 +27,19 @@ dependencies = [
|
|
|
13
27
|
"httpx>=0.27.0",
|
|
14
28
|
]
|
|
15
29
|
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://sofias.ai/"
|
|
32
|
+
Documentation = "https://docs.sofias.ai/"
|
|
33
|
+
Repository = "https://github.com/Sofias-ai/sofias-sdk-lite"
|
|
34
|
+
Changelog = "https://github.com/Sofias-ai/sofias-sdk-lite/blob/main/CHANGELOG.md"
|
|
35
|
+
|
|
16
36
|
[project.optional-dependencies]
|
|
17
37
|
otel = [
|
|
18
38
|
"opentelemetry-api>=1.20.0",
|
|
19
39
|
]
|
|
20
40
|
docs = [
|
|
41
|
+
# MkDocs 2.0 drops the plugin system this site relies on.
|
|
42
|
+
"mkdocs>=1.6,<2",
|
|
21
43
|
"mkdocs-material>=9.5",
|
|
22
44
|
"mkdocstrings[python]>=0.25",
|
|
23
45
|
]
|
|
@@ -91,7 +91,7 @@ from sofias_sdk_lite.errors import (
|
|
|
91
91
|
ToolExecutionError,
|
|
92
92
|
)
|
|
93
93
|
from sofias_sdk_lite.llm import (
|
|
94
|
-
|
|
94
|
+
GatewayLLM,
|
|
95
95
|
LLMCallable,
|
|
96
96
|
LLMGatewayConfig,
|
|
97
97
|
LLMResponse,
|
|
@@ -230,7 +230,7 @@ __all__ = [
|
|
|
230
230
|
"StreamableLLMCallable",
|
|
231
231
|
"LLMResponse",
|
|
232
232
|
# LLM clients + factory
|
|
233
|
-
"
|
|
233
|
+
"GatewayLLM",
|
|
234
234
|
"OpenAICompatibleLLM",
|
|
235
235
|
"LLMGatewayConfig",
|
|
236
236
|
"create_llm",
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/agent_settings.py
RENAMED
|
@@ -68,13 +68,13 @@ class BaseAgentSettings(AgentSettings):
|
|
|
68
68
|
"""Model identifier or gateway alias. Empty means "no LLM configured here"."""
|
|
69
69
|
|
|
70
70
|
router_url: str = ""
|
|
71
|
-
"""OpenAI-compatible gateway root
|
|
71
|
+
"""OpenAI-compatible gateway root, injected by the platform. Required to build an LLM."""
|
|
72
72
|
|
|
73
73
|
router_api_key: SecretStr = SecretStr("")
|
|
74
74
|
"""Gateway bearer token. Empty sends unauthenticated requests."""
|
|
75
75
|
|
|
76
76
|
router_provider: str = ""
|
|
77
|
-
"""Adapter flavour: ``"
|
|
77
|
+
"""Adapter flavour: ``"sofias"`` (default when empty) or an OpenAI-compatible label."""
|
|
78
78
|
|
|
79
79
|
@property
|
|
80
80
|
def has_llm_gateway(self) -> bool:
|
|
@@ -5,19 +5,19 @@ Two layers:
|
|
|
5
5
|
- **Protocol** (`LLMCallable`, `StreamableLLMCallable`, `LLMResponse`,
|
|
6
6
|
`TokenUsage`, `ToolSpec`, ...): the contract `LLMNode` drives. Wrap any
|
|
7
7
|
client in it if you need something the bundled adapters do not cover.
|
|
8
|
-
- **Batteries** (`create_llm`, `
|
|
9
|
-
`LLMGatewayConfig`): a ready-made client for the Sofias
|
|
8
|
+
- **Batteries** (`create_llm`, `GatewayLLM`, `OpenAICompatibleLLM`,
|
|
9
|
+
`LLMGatewayConfig`): a ready-made client for the Sofias gateway (or
|
|
10
10
|
any OpenAI-compatible endpoint) resolved from settings or the environment,
|
|
11
11
|
so agent code never constructs an LLM client by hand.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
-
from sofias_sdk_lite.llm.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
from sofias_sdk_lite.llm.gateway import (
|
|
17
|
+
DEFAULT_GATEWAY_MAX_TOKENS,
|
|
18
|
+
PROVIDER_GATEWAY,
|
|
19
|
+
GatewayLLM,
|
|
20
|
+
normalize_gateway_base_url,
|
|
21
21
|
)
|
|
22
22
|
from sofias_sdk_lite.llm.factory import (
|
|
23
23
|
ENV_LLM_API_KEY,
|
|
@@ -52,11 +52,11 @@ __all__ = [
|
|
|
52
52
|
"ToolCall",
|
|
53
53
|
"ToolSpec",
|
|
54
54
|
# Clients
|
|
55
|
-
"
|
|
55
|
+
"GatewayLLM",
|
|
56
56
|
"OpenAICompatibleLLM",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
57
|
+
"DEFAULT_GATEWAY_MAX_TOKENS",
|
|
58
|
+
"PROVIDER_GATEWAY",
|
|
59
|
+
"normalize_gateway_base_url",
|
|
60
60
|
# Factory
|
|
61
61
|
"LLMGatewayConfig",
|
|
62
62
|
"create_llm",
|
|
@@ -6,10 +6,9 @@ Resolution order for `create_llm`:
|
|
|
6
6
|
2. The agent's runtime settings (`BaseAgentSettings.model_name`,
|
|
7
7
|
``router_url``, ``router_api_key``), which the platform injects per task.
|
|
8
8
|
3. Environment variables: ``SOFIAS_LLM_MODEL``, ``SOFIAS_LLM_BASE_URL``,
|
|
9
|
-
``SOFIAS_LLM_API_KEY``, ``SOFIAS_LLM_PROVIDER
|
|
10
|
-
``
|
|
11
|
-
|
|
12
|
-
``MODEL_NAME`` / ``ROUTER_URL`` / ``ROUTER_API_KEY``.
|
|
9
|
+
``SOFIAS_LLM_API_KEY``, ``SOFIAS_LLM_PROVIDER`` (what the Sofias platform
|
|
10
|
+
injects into every agent container); then ``MODEL_NAME`` / ``ROUTER_URL`` /
|
|
11
|
+
``ROUTER_API_KEY``, the same keys the agent settings use.
|
|
13
12
|
|
|
14
13
|
`AgentBuilder.build()` calls `resolve_default_llm` when no `with_llm()` was
|
|
15
14
|
given, and `AgentRunner` scopes the per-task settings-derived client with
|
|
@@ -27,7 +26,7 @@ from typing import Any, Final
|
|
|
27
26
|
from pydantic import BaseModel, ConfigDict, SecretStr
|
|
28
27
|
|
|
29
28
|
from sofias_sdk_lite.errors.exceptions import LLMConfigurationError
|
|
30
|
-
from sofias_sdk_lite.llm.
|
|
29
|
+
from sofias_sdk_lite.llm.gateway import PROVIDER_GATEWAY, GatewayLLM
|
|
31
30
|
from sofias_sdk_lite.llm.openai_compatible import OpenAICompatibleLLM
|
|
32
31
|
from sofias_sdk_lite.llm.protocol import LLMCallable, StreamableLLMCallable
|
|
33
32
|
from sofias_sdk_lite.observability._log import get_logger
|
|
@@ -53,16 +52,12 @@ ENV_LLM_BASE_URL: Final[str] = "SOFIAS_LLM_BASE_URL"
|
|
|
53
52
|
ENV_LLM_API_KEY: Final[str] = "SOFIAS_LLM_API_KEY"
|
|
54
53
|
ENV_LLM_PROVIDER: Final[str] = "SOFIAS_LLM_PROVIDER"
|
|
55
54
|
|
|
56
|
-
# Fallback names, tried
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
_ENV_FALLBACK_MODEL: Final[tuple[str, ...]] = ("BIFROST_MODEL", "MODEL_NAME")
|
|
62
|
-
_ENV_FALLBACK_BASE_URL: Final[tuple[str, ...]] = ("BIFROST_BASE_URL", "ROUTER_URL")
|
|
63
|
-
_ENV_FALLBACK_API_KEY: Final[tuple[str, ...]] = ("BIFROST_API_KEY", "ROUTER_API_KEY")
|
|
55
|
+
# Fallback names, tried after the SOFIAS_LLM_* variables: the same keys the
|
|
56
|
+
# agent settings use, so a container that already exports them needs no renaming.
|
|
57
|
+
_ENV_FALLBACK_MODEL: Final[tuple[str, ...]] = ("MODEL_NAME",)
|
|
58
|
+
_ENV_FALLBACK_BASE_URL: Final[tuple[str, ...]] = ("ROUTER_URL",)
|
|
59
|
+
_ENV_FALLBACK_API_KEY: Final[tuple[str, ...]] = ("ROUTER_API_KEY",)
|
|
64
60
|
|
|
65
|
-
PROVIDER_BIFROST: Final[str] = "bifrost"
|
|
66
61
|
_OPENAI_COMPATIBLE_PROVIDERS: Final[frozenset[str]] = frozenset(
|
|
67
62
|
{"openai", "openai-compatible", "generic", "litellm", "openrouter", "vllm"}
|
|
68
63
|
)
|
|
@@ -71,8 +66,9 @@ _OPENAI_COMPATIBLE_PROVIDERS: Final[frozenset[str]] = frozenset(
|
|
|
71
66
|
class LLMGatewayConfig(BaseModel):
|
|
72
67
|
"""Everything needed to build an LLM client.
|
|
73
68
|
|
|
74
|
-
``model``
|
|
75
|
-
|
|
69
|
+
``model`` names the model; ``base_url`` must be set before `create_llm`
|
|
70
|
+
can build a client (the platform injects it). ``provider`` defaults to
|
|
71
|
+
``"sofias"``, the Sofias gateway.
|
|
76
72
|
"""
|
|
77
73
|
|
|
78
74
|
model_config = ConfigDict(frozen=True)
|
|
@@ -80,14 +76,14 @@ class LLMGatewayConfig(BaseModel):
|
|
|
80
76
|
model: str
|
|
81
77
|
"""Model identifier or gateway alias (e.g. ``"default"``)."""
|
|
82
78
|
|
|
83
|
-
base_url: str =
|
|
84
|
-
"""OpenAI-compatible API root."""
|
|
79
|
+
base_url: str = ""
|
|
80
|
+
"""OpenAI-compatible API root. Injected by the platform; required to build a client."""
|
|
85
81
|
|
|
86
82
|
api_key: SecretStr = SecretStr("")
|
|
87
83
|
"""Bearer token. Empty sends no ``Authorization`` header."""
|
|
88
84
|
|
|
89
|
-
provider: str =
|
|
90
|
-
"""``"
|
|
85
|
+
provider: str = PROVIDER_GATEWAY
|
|
86
|
+
"""``"sofias"`` or any OpenAI-compatible label (``"openai"``, ``"litellm"``, ...)."""
|
|
91
87
|
|
|
92
88
|
temperature: float | None = None
|
|
93
89
|
"""Sampling temperature; ``None`` keeps the adapter default."""
|
|
@@ -102,7 +98,7 @@ class LLMGatewayConfig(BaseModel):
|
|
|
102
98
|
"""Retries on transient failures."""
|
|
103
99
|
|
|
104
100
|
reasoning_effort: str | None = None
|
|
105
|
-
"""
|
|
101
|
+
"""Sofias gateway only: overrides the per-model-family default."""
|
|
106
102
|
|
|
107
103
|
def with_overrides(self, **overrides: Any) -> LLMGatewayConfig:
|
|
108
104
|
"""Return a copy with the non-``None`` overrides applied."""
|
|
@@ -123,16 +119,16 @@ def _first_env(env: Mapping[str, str], primary: str, fallbacks: tuple[str, ...])
|
|
|
123
119
|
def gateway_config_from_env(environ: Mapping[str, str] | None = None) -> LLMGatewayConfig | None:
|
|
124
120
|
"""Read gateway configuration from the environment; ``None`` if no model is set.
|
|
125
121
|
|
|
126
|
-
``SOFIAS_LLM_*`` wins, then
|
|
127
|
-
``
|
|
122
|
+
``SOFIAS_LLM_*`` wins, then ``MODEL_NAME`` / ``ROUTER_URL`` /
|
|
123
|
+
``ROUTER_API_KEY``.
|
|
128
124
|
"""
|
|
129
125
|
env = os.environ if environ is None else environ
|
|
130
126
|
model = _first_env(env, ENV_LLM_MODEL, _ENV_FALLBACK_MODEL)
|
|
131
127
|
if not model:
|
|
132
128
|
return None
|
|
133
|
-
base_url = _first_env(env, ENV_LLM_BASE_URL, _ENV_FALLBACK_BASE_URL)
|
|
129
|
+
base_url = _first_env(env, ENV_LLM_BASE_URL, _ENV_FALLBACK_BASE_URL)
|
|
134
130
|
api_key = _first_env(env, ENV_LLM_API_KEY, _ENV_FALLBACK_API_KEY)
|
|
135
|
-
provider = env.get(ENV_LLM_PROVIDER) or
|
|
131
|
+
provider = env.get(ENV_LLM_PROVIDER) or PROVIDER_GATEWAY
|
|
136
132
|
return LLMGatewayConfig(
|
|
137
133
|
model=model, base_url=base_url, api_key=SecretStr(api_key), provider=provider
|
|
138
134
|
)
|
|
@@ -149,10 +145,10 @@ def gateway_config_from_settings(settings: Any) -> LLMGatewayConfig | None:
|
|
|
149
145
|
model = getattr(settings, "model_name", "") or ""
|
|
150
146
|
if not model:
|
|
151
147
|
return None
|
|
152
|
-
base_url = getattr(settings, "router_url", "") or
|
|
148
|
+
base_url = getattr(settings, "router_url", "") or ""
|
|
153
149
|
raw_key = getattr(settings, "router_api_key", "")
|
|
154
150
|
api_key = raw_key.get_secret_value() if isinstance(raw_key, SecretStr) else str(raw_key or "")
|
|
155
|
-
provider = getattr(settings, "router_provider", "") or
|
|
151
|
+
provider = getattr(settings, "router_provider", "") or PROVIDER_GATEWAY
|
|
156
152
|
return LLMGatewayConfig(
|
|
157
153
|
model=model, base_url=base_url, api_key=SecretStr(api_key), provider=provider
|
|
158
154
|
)
|
|
@@ -204,14 +200,23 @@ def create_llm(
|
|
|
204
200
|
source: An `LLMGatewayConfig`, an agent settings object exposing
|
|
205
201
|
``model_name`` / ``router_url`` / ``router_api_key``, or ``None``
|
|
206
202
|
to fall back to keyword arguments and then the environment.
|
|
207
|
-
model
|
|
208
|
-
|
|
203
|
+
model: Overrides the resolved model identifier or gateway alias.
|
|
204
|
+
base_url: Overrides the resolved gateway URL.
|
|
205
|
+
api_key: Overrides the resolved bearer token.
|
|
206
|
+
provider: Overrides the adapter flavour (``"sofias"`` or an
|
|
207
|
+
OpenAI-compatible label).
|
|
208
|
+
temperature: Overrides the sampling temperature.
|
|
209
|
+
max_tokens: Overrides the output budget.
|
|
210
|
+
timeout: Overrides the per-request timeout in seconds.
|
|
211
|
+
max_retries: Overrides the number of retries on transient failures.
|
|
212
|
+
reasoning_effort: Overrides the per-model-family default (Sofias
|
|
213
|
+
gateway only).
|
|
209
214
|
**adapter_kwargs: Forwarded to the adapter constructor (e.g.
|
|
210
215
|
``transport=`` for tests, ``default_headers=``).
|
|
211
216
|
|
|
212
217
|
Raises:
|
|
213
|
-
LLMConfigurationError: If no model can be resolved or
|
|
214
|
-
is unknown.
|
|
218
|
+
LLMConfigurationError: If no model or gateway URL can be resolved, or
|
|
219
|
+
the provider is unknown.
|
|
215
220
|
"""
|
|
216
221
|
config = _resolve_config(
|
|
217
222
|
source,
|
|
@@ -246,12 +251,20 @@ def create_llm(
|
|
|
246
251
|
if config.temperature is not None:
|
|
247
252
|
common["temperature"] = config.temperature
|
|
248
253
|
|
|
254
|
+
if not config.base_url.strip():
|
|
255
|
+
raise LLMConfigurationError(
|
|
256
|
+
f"No LLM gateway URL configured for model '{config.model}'. On the Sofias "
|
|
257
|
+
f"platform {ENV_LLM_BASE_URL} is injected automatically; to run locally, "
|
|
258
|
+
"export it or install a fake LLM with default_llm(...).",
|
|
259
|
+
missing=["base_url"],
|
|
260
|
+
)
|
|
261
|
+
|
|
249
262
|
provider_name = config.provider.lower()
|
|
250
|
-
if provider_name ==
|
|
251
|
-
return
|
|
263
|
+
if provider_name == PROVIDER_GATEWAY:
|
|
264
|
+
return GatewayLLM(reasoning_effort=config.reasoning_effort, **common)
|
|
252
265
|
if provider_name in _OPENAI_COMPATIBLE_PROVIDERS:
|
|
253
266
|
return OpenAICompatibleLLM(provider=provider_name, **common)
|
|
254
|
-
supported = ", ".join(sorted({
|
|
267
|
+
supported = ", ".join(sorted({PROVIDER_GATEWAY, *_OPENAI_COMPATIBLE_PROVIDERS}))
|
|
255
268
|
raise LLMConfigurationError(
|
|
256
269
|
f"Unknown LLM provider '{config.provider}'. Supported: {supported}",
|
|
257
270
|
missing=["provider"],
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Sofias gateway client.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
adapter is `OpenAICompatibleLLM` with the
|
|
3
|
+
The Sofias platform gives every agent access to its models through an
|
|
4
|
+
OpenAI-compatible gateway. This adapter is `OpenAICompatibleLLM` with the
|
|
5
|
+
gateway's defaults baked in:
|
|
5
6
|
|
|
6
|
-
- base URL ``http://bifrost:8080/v1`` (the in-cluster service name);
|
|
7
7
|
- a per-model-family ``reasoning_effort`` so thinking channels are switched
|
|
8
8
|
off (or minimised) unless the caller asks otherwise;
|
|
9
9
|
- a generous ``max_tokens`` budget, because structured output is paid out
|
|
10
10
|
of that budget and a truncated JSON document does not parse.
|
|
11
|
+
|
|
12
|
+
There is no default address: when the agent runs on the platform the gateway
|
|
13
|
+
URL is injected for it (``SOFIAS_LLM_BASE_URL``), and to run locally against a
|
|
14
|
+
real gateway you set it yourself.
|
|
11
15
|
"""
|
|
12
16
|
|
|
13
17
|
from __future__ import annotations
|
|
@@ -17,19 +21,20 @@ from urllib.parse import urlsplit
|
|
|
17
21
|
|
|
18
22
|
import httpx
|
|
19
23
|
|
|
24
|
+
from sofias_sdk_lite.errors.exceptions import LLMConfigurationError
|
|
20
25
|
from sofias_sdk_lite.llm.openai_compatible import OpenAICompatibleLLM
|
|
21
26
|
|
|
22
27
|
__all__ = [
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
28
|
+
"DEFAULT_GATEWAY_MAX_TOKENS",
|
|
29
|
+
"GatewayLLM",
|
|
30
|
+
"PROVIDER_GATEWAY",
|
|
31
|
+
"normalize_gateway_base_url",
|
|
27
32
|
]
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
"""
|
|
34
|
+
PROVIDER_GATEWAY: Final[str] = "sofias"
|
|
35
|
+
"""Provider label that selects this adapter in `create_llm`."""
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
DEFAULT_GATEWAY_MAX_TOKENS: Final[int] = 16_384
|
|
33
38
|
"""Output budget sent when neither the constructor nor the caller sets one.
|
|
34
39
|
|
|
35
40
|
A single budget on purpose: splitting it by reasoning mode produced a family
|
|
@@ -39,9 +44,9 @@ billed, so a generous cap costs nothing on short completions.
|
|
|
39
44
|
|
|
40
45
|
# Per-family default ``reasoning_effort`` when the caller passes none.
|
|
41
46
|
# ``"none"`` turns the thinking channel off, ``"low"`` keeps it minimal.
|
|
42
|
-
# - gpt-oss reasons by default and
|
|
43
|
-
# - deepseek:
|
|
44
|
-
# - qwen: thinking
|
|
47
|
+
# - gpt-oss reasons by default and "none" is not accepted everywhere -> "low".
|
|
48
|
+
# - deepseek: "none" is not accepted everywhere -> "low".
|
|
49
|
+
# - qwen: thinking behaves inconsistently unless disabled -> "none" explicitly.
|
|
45
50
|
# - glm: "none" makes the model emit its monologue inside content -> "low".
|
|
46
51
|
_REASONING_EFFORT_BY_FAMILY: Final[dict[str, str]] = {
|
|
47
52
|
"gpt-oss": "low",
|
|
@@ -51,43 +56,55 @@ _REASONING_EFFORT_BY_FAMILY: Final[dict[str, str]] = {
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
# Fallback for models outside the table, including the ``"default"`` gateway
|
|
54
|
-
# alias most agents send: ``"low"`` is accepted by every
|
|
59
|
+
# alias most agents send: ``"low"`` is accepted by every model family.
|
|
55
60
|
_REASONING_EFFORT_FALLBACK: Final[str] = "low"
|
|
56
61
|
|
|
57
62
|
|
|
58
|
-
def
|
|
59
|
-
"""Return the OpenAI-compatible API root for a
|
|
63
|
+
def normalize_gateway_base_url(base_url: str) -> str:
|
|
64
|
+
"""Return the OpenAI-compatible API root for a gateway host.
|
|
65
|
+
|
|
66
|
+
The gateway can be configured either as a bare host
|
|
67
|
+
(``https://gateway.example``) or as the API root
|
|
68
|
+
(``https://gateway.example/v1``). The OpenAI surface lives under ``/v1``,
|
|
69
|
+
so a URL with no path gets it appended; any URL that already has a path is
|
|
70
|
+
trusted as-is.
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
(``https://bifrost.example/v1``). Bifrost serves the OpenAI surface under
|
|
64
|
-
``/v1``, so a URL with no path gets it appended; any URL that already has
|
|
65
|
-
a path is trusted as-is. Empty input falls back to the in-cluster default.
|
|
72
|
+
Raises:
|
|
73
|
+
LLMConfigurationError: If ``base_url`` is empty.
|
|
66
74
|
"""
|
|
67
|
-
url = (base_url or
|
|
75
|
+
url = (base_url or "").strip().rstrip("/")
|
|
76
|
+
if not url:
|
|
77
|
+
raise LLMConfigurationError(
|
|
78
|
+
"No LLM gateway URL configured. On the Sofias platform "
|
|
79
|
+
"SOFIAS_LLM_BASE_URL is injected automatically; to run locally, "
|
|
80
|
+
"export it or install a fake LLM with default_llm(...).",
|
|
81
|
+
missing=["base_url"],
|
|
82
|
+
)
|
|
68
83
|
if urlsplit(url).path in ("", "/"):
|
|
69
84
|
return f"{url}/v1"
|
|
70
85
|
return url
|
|
71
86
|
|
|
72
87
|
|
|
73
|
-
class
|
|
74
|
-
"""`StreamableLLMCallable` for the
|
|
88
|
+
class GatewayLLM(OpenAICompatibleLLM):
|
|
89
|
+
"""`StreamableLLMCallable` for the Sofias gateway.
|
|
75
90
|
|
|
76
91
|
Args:
|
|
77
|
-
api_key:
|
|
92
|
+
api_key: Gateway API key (empty string sends no ``Authorization``).
|
|
78
93
|
model: Model identifier or gateway alias (e.g. ``"default"``).
|
|
79
|
-
base_url: Gateway host or API root;
|
|
80
|
-
|
|
81
|
-
`normalize_bifrost_base_url`).
|
|
94
|
+
base_url: Gateway host or API root. Required; a bare host gets
|
|
95
|
+
``/v1`` appended (see `normalize_gateway_base_url`).
|
|
82
96
|
temperature: Sampling temperature.
|
|
83
97
|
max_retries: Retries on transient failures.
|
|
84
98
|
timeout: Per-request timeout in seconds.
|
|
85
99
|
reasoning_effort: ``None`` applies the per-family default; a non-empty
|
|
86
100
|
string is sent as-is on every request; ``""`` disables injection
|
|
87
|
-
so the
|
|
88
|
-
max_tokens: Output budget; ``None`` applies `
|
|
101
|
+
so the model default applies.
|
|
102
|
+
max_tokens: Output budget; ``None`` applies `DEFAULT_GATEWAY_MAX_TOKENS`.
|
|
89
103
|
default_headers: Extra headers sent on every request.
|
|
90
104
|
transport: Optional ``httpx`` transport (for tests).
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
LLMConfigurationError: If ``base_url`` is empty.
|
|
91
108
|
"""
|
|
92
109
|
|
|
93
110
|
def __init__(
|
|
@@ -95,7 +112,7 @@ class BifrostLLM(OpenAICompatibleLLM):
|
|
|
95
112
|
*,
|
|
96
113
|
api_key: str,
|
|
97
114
|
model: str,
|
|
98
|
-
base_url: str
|
|
115
|
+
base_url: str,
|
|
99
116
|
temperature: float = 0.1,
|
|
100
117
|
max_retries: int = 3,
|
|
101
118
|
timeout: float = 120.0,
|
|
@@ -106,9 +123,9 @@ class BifrostLLM(OpenAICompatibleLLM):
|
|
|
106
123
|
) -> None:
|
|
107
124
|
super().__init__(
|
|
108
125
|
api_key=api_key,
|
|
109
|
-
base_url=
|
|
126
|
+
base_url=normalize_gateway_base_url(base_url),
|
|
110
127
|
model=model,
|
|
111
|
-
provider=
|
|
128
|
+
provider=PROVIDER_GATEWAY,
|
|
112
129
|
temperature=temperature,
|
|
113
130
|
max_retries=max_retries,
|
|
114
131
|
timeout=timeout,
|
|
@@ -126,10 +143,10 @@ class BifrostLLM(OpenAICompatibleLLM):
|
|
|
126
143
|
return _REASONING_EFFORT_FALLBACK
|
|
127
144
|
|
|
128
145
|
def _augment_request_kwargs(self, kwargs: dict[str, Any]) -> None:
|
|
129
|
-
"""Inject
|
|
146
|
+
"""Inject gateway defaults; values already present in the request win."""
|
|
130
147
|
if self._reasoning_effort is None:
|
|
131
148
|
model = kwargs.get("model") or self._model
|
|
132
149
|
kwargs.setdefault("reasoning_effort", self._default_reasoning_effort(str(model)))
|
|
133
150
|
elif self._reasoning_effort:
|
|
134
151
|
kwargs.setdefault("reasoning_effort", self._reasoning_effort)
|
|
135
|
-
kwargs.setdefault("max_tokens",
|
|
152
|
+
kwargs.setdefault("max_tokens", DEFAULT_GATEWAY_MAX_TOKENS)
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/llm/openai_compatible.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""OpenAI-compatible chat-completions client implementing `StreamableLLMCallable`.
|
|
2
2
|
|
|
3
3
|
Any gateway or provider that speaks the OpenAI ``/chat/completions`` wire
|
|
4
|
-
format (
|
|
4
|
+
format (the Sofias gateway, LiteLLM, OpenRouter, vLLM, OpenAI itself, ...) works with
|
|
5
5
|
this adapter. It is built on ``httpx`` only, so the SDK stays free of any
|
|
6
6
|
vendor SDK.
|
|
7
7
|
|
|
@@ -71,7 +71,7 @@ class OpenAICompatibleLLM:
|
|
|
71
71
|
``MockTransport`` here).
|
|
72
72
|
"""
|
|
73
73
|
|
|
74
|
-
GATEWAY_PROVIDERS: ClassVar[frozenset[str]] = frozenset({"
|
|
74
|
+
GATEWAY_PROVIDERS: ClassVar[frozenset[str]] = frozenset({"sofias", "litellm", "openrouter"})
|
|
75
75
|
|
|
76
76
|
def __init__(
|
|
77
77
|
self,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""The `LLMCallable` protocol and its request/response types.
|
|
2
2
|
|
|
3
3
|
This module defines the contract `LLMNode` drives. The SDK ships two
|
|
4
|
-
implementations (`sofias_sdk_lite.llm.
|
|
4
|
+
implementations (`sofias_sdk_lite.llm.GatewayLLM` for the Sofias gateway and
|
|
5
5
|
`sofias_sdk_lite.llm.OpenAICompatibleLLM` for any OpenAI-style endpoint) and a
|
|
6
6
|
factory (`sofias_sdk_lite.llm.create_llm`) that picks one from settings or the
|
|
7
7
|
environment. Implement the protocol yourself only when you need a client those
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/__init__.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Observability protocols for sofias_sdk_lite: tracing and events.
|
|
2
2
|
|
|
3
|
-
Protocol-only — no concrete OTLP
|
|
3
|
+
Protocol-only — no concrete OTLP exporter or message broker client
|
|
4
4
|
ships here. Bring your own tracing backend (OpenTelemetry, Jaeger,
|
|
5
5
|
Datadog, ...) and event broker (RabbitMQ, Kafka, ...) by implementing the
|
|
6
6
|
protocols below.
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/tracing.py
RENAMED
|
@@ -6,7 +6,7 @@ protocols the SDK expects from an external tracing system, the
|
|
|
6
6
|
node, tool, and LLM operations), and no-op / logging-only implementations
|
|
7
7
|
for when a real tracing backend isn't configured.
|
|
8
8
|
|
|
9
|
-
Protocol-only: no concrete OpenTelemetry/OTLP
|
|
9
|
+
Protocol-only: no concrete OpenTelemetry/OTLP exporter is vendored.
|
|
10
10
|
Bring your own backend by implementing ``TracingProvider``.
|
|
11
11
|
"""
|
|
12
12
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/execution_context.py
RENAMED
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/agent/response_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/config/runtime_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/agent_contracts.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/base_contracts.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/node_contracts.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/contracts/strict_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/errors/circuit_breaker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/aggregator_config.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/aggregator_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_config.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_node.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/delegation_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/llm_node_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/planning_models.py
RENAMED
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/nodes/prompt_assembler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/observability/trace_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/rabbitmq/delegation_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/state/conversation_state.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sofias_sdk_lite-0.1.5 → sofias_sdk_lite-0.2.0}/src/sofias_sdk_lite/workflows/streaming_chat.py
RENAMED
|
File without changes
|