soliplex 0.75.4__tar.gz → 0.76__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.
- {soliplex-0.75.4/src/soliplex.egg-info → soliplex-0.76}/PKG-INFO +34 -34
- {soliplex-0.75.4 → soliplex-0.76}/pyproject.toml +3 -3
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agents.py +24 -6
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/agents.py +99 -43
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/installation.py +38 -8
- soliplex-0.76/src/soliplex/config/meta.py +687 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/middleware.py +17 -5
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/rag.py +78 -19
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/rooms.py +24 -13
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/routing.py +78 -37
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/secrets.py +54 -3
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/skills.py +133 -11
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/tools.py +50 -12
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/agui.py +2 -2
- {soliplex-0.75.4 → soliplex-0.76/src/soliplex.egg-info}/PKG-INFO +34 -34
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex.egg-info/requires.txt +2 -2
- soliplex-0.75.4/src/soliplex/config/meta.py +0 -398
- {soliplex-0.75.4 → soliplex-0.76}/LICENSE +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/MANIFEST.in +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/README.md +31 -31
- {soliplex-0.75.4 → soliplex-0.76}/example/skills/bare-bones/SKILL.md +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/example/skills/stanzas/SKILL.md +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/setup.cfg +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/skills/soliplex-docs/SKILL.md +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agui/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agui/parser.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agui/persistence.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agui/schema.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/agui/util.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/authn.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/authz/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/authz/persistence.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/authz/schema.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/capabilities/filesystem.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/capabilities/rag_audit.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/admin_users.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/ask.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/audit.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/cli_util.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/main.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/ollama.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/room_authz.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/serve.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/cli/types.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/completions.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/_utils.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/agui.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/authsystem.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/completions.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/exceptions.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/logfire.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/config/quizzes.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/examples.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/installation.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/log_ingest.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/loggers.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/main.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/mcp_auth.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/mcp_client.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/mcp_server.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/models.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/ollama.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/quizzes.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/rag_audit.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/sandbox_audit.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/secrets.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/skills/bwrap_sandbox/SKILL.md +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/skills/bwrap_sandbox/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/titles.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tools/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tools/agui_run_feedback.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tools/file_uploads.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tools/rag.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tui/cli.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tui/main.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tui/rest_api.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/tui/serve.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/util.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/__init__.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/authn.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/authz.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/completions.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/installation.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/log_ingest.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/quizzes.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/room_file_uploads.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/rooms.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/sandbox_workdirs.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/stats.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/streaming.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex/views/thread_file_uploads.py +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex.egg-info/SOURCES.txt +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex.egg-info/dependency_links.txt +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex.egg-info/entry_points.txt +0 -0
- {soliplex-0.75.4 → soliplex-0.76}/src/soliplex.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soliplex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.76
|
|
4
4
|
Summary: An AI-powered Retrieval-Augmented Generation (RAG) system with a modern web interface.
|
|
5
5
|
Author-email: Enfold <info@enfoldsystems.net>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -13,13 +13,13 @@ Requires-Dist: aiohttp<3.15,>=3.14.2
|
|
|
13
13
|
Requires-Dist: aiosqlite
|
|
14
14
|
Requires-Dist: alembic<1.19,>=1.18.4
|
|
15
15
|
Requires-Dist: authlib
|
|
16
|
-
Requires-Dist: bubble-sandbox<0.
|
|
16
|
+
Requires-Dist: bubble-sandbox<0.12,>=0.11.0
|
|
17
17
|
Requires-Dist: cryptography>=50.0.0
|
|
18
18
|
Requires-Dist: fastapi
|
|
19
19
|
Requires-Dist: fastmcp<3.4,>=3.3.0
|
|
20
20
|
Requires-Dist: fakeredis!=2.35.0
|
|
21
21
|
Requires-Dist: greenlet
|
|
22
|
-
Requires-Dist: haiku.rag-slim<0.
|
|
22
|
+
Requires-Dist: haiku.rag-slim<0.75,>=0.74.0
|
|
23
23
|
Requires-Dist: idna>=3.15
|
|
24
24
|
Requires-Dist: itsdangerous
|
|
25
25
|
Requires-Dist: joserfc<1.7,>=1.6.7
|
|
@@ -46,6 +46,17 @@ Dynamic: license-file
|
|
|
46
46
|
|
|
47
47
|
An AI-powered Retrieval-Augmented Generation (RAG) system with a modern web interface.
|
|
48
48
|
|
|
49
|
+
## Overview
|
|
50
|
+
|
|
51
|
+
Soliplex is a self-hosted, full-stack RAG/AI system: a FastAPI backend
|
|
52
|
+
(a thin configuration and authentication layer around
|
|
53
|
+
[pydantic-ai](https://ai.pydantic.dev/)), a hardened
|
|
54
|
+
[Flutter client](https://github.com/soliplex/frontend), and a terminal UI.
|
|
55
|
+
|
|
56
|
+
See the [Overview](docs/overview.md) for what Soliplex is, when (and when not)
|
|
57
|
+
to use it, how it compares to alternatives, and the full feature and
|
|
58
|
+
architecture breakdown.
|
|
59
|
+
|
|
49
60
|
## Quickstart
|
|
50
61
|
|
|
51
62
|
The fastest way to stand up and run your own Soliplex is to let a
|
|
@@ -123,17 +134,6 @@ and then *"What is a Soliplex installation?"*
|
|
|
123
134
|
> and developing on the backend itself. If you only want to *use* Soliplex,
|
|
124
135
|
> the two skills above are all you need.
|
|
125
136
|
|
|
126
|
-
## Overview
|
|
127
|
-
|
|
128
|
-
Soliplex is a self-hosted, full-stack RAG/AI system: a FastAPI backend
|
|
129
|
-
(a thin configuration and authentication layer around
|
|
130
|
-
[pydantic-ai](https://ai.pydantic.dev/)), a hardened
|
|
131
|
-
[Flutter client](https://github.com/soliplex/frontend), and a terminal UI.
|
|
132
|
-
|
|
133
|
-
See the [Overview](docs/overview.md) for what Soliplex is, when (and when not)
|
|
134
|
-
to use it, how it compares to alternatives, and the full feature and
|
|
135
|
-
architecture breakdown.
|
|
136
|
-
|
|
137
137
|
## Running from source
|
|
138
138
|
|
|
139
139
|
The skills-based [Quickstart](#quickstart) above is the recommended path for
|
|
@@ -150,6 +150,26 @@ most users. To run Soliplex from a checkout of this repository instead, see:
|
|
|
150
150
|
- [Client Setup](docs/client.md) -- run the Flutter web client.
|
|
151
151
|
- [Docker Deployment](docs/docker.md) -- run the full stack with Docker Compose.
|
|
152
152
|
|
|
153
|
+
## Documentation
|
|
154
|
+
|
|
155
|
+
The full documentation set lives in [`docs/`](docs/) and is published as a
|
|
156
|
+
[Zensical site](https://soliplex.github.io/soliplex/):
|
|
157
|
+
|
|
158
|
+
> **Tip:** the same documentation is also available as the `soliplex-docs`
|
|
159
|
+
> Agent Skill, so a skills-compatible agent can answer questions straight from
|
|
160
|
+
> it -- see the [Quickstart](#quickstart) above.
|
|
161
|
+
|
|
162
|
+
- [Overview](docs/overview.md)
|
|
163
|
+
- [Prerequisites](docs/prerequisites.md)
|
|
164
|
+
- [Server Setup](docs/server/index.md) and
|
|
165
|
+
[CLI Reference](docs/server/cli.md)
|
|
166
|
+
- [Client Setup](docs/client.md)
|
|
167
|
+
- [Docker Deployment](docs/docker.md)
|
|
168
|
+
- [RAG Database](docs/rag.md)
|
|
169
|
+
- [Documentation Skill](docs/docs-skill.md)
|
|
170
|
+
- [Configuration](docs/config/)
|
|
171
|
+
- [Usage](docs/usage.md)
|
|
172
|
+
|
|
153
173
|
## Configuration
|
|
154
174
|
|
|
155
175
|
Soliplex is configured by a tree of YAML files rooted at an
|
|
@@ -173,26 +193,6 @@ Soliplex is configured by a tree of YAML files rooted at an
|
|
|
173
193
|
[Console logging](docs/config/logging.md),
|
|
174
194
|
[SQLAlchemy DBURIs](docs/config/dburis.md)
|
|
175
195
|
|
|
176
|
-
## Documentation
|
|
177
|
-
|
|
178
|
-
The full documentation set lives in [`docs/`](docs/) and is published as a
|
|
179
|
-
[Zensical site](https://soliplex.github.io/soliplex/):
|
|
180
|
-
|
|
181
|
-
> **Tip:** the same documentation is also available as the `soliplex-docs`
|
|
182
|
-
> Agent Skill, so a skills-compatible agent can answer questions straight from
|
|
183
|
-
> it -- see the [Quickstart](#quickstart) above.
|
|
184
|
-
|
|
185
|
-
- [Overview](docs/overview.md)
|
|
186
|
-
- [Prerequisites](docs/prerequisites.md)
|
|
187
|
-
- [Server Setup](docs/server/index.md) and
|
|
188
|
-
[CLI Reference](docs/server/cli.md)
|
|
189
|
-
- [Client Setup](docs/client.md)
|
|
190
|
-
- [Docker Deployment](docs/docker.md)
|
|
191
|
-
- [RAG Database](docs/rag.md)
|
|
192
|
-
- [Documentation Skill](docs/docs-skill.md)
|
|
193
|
-
- [Configuration](docs/config/)
|
|
194
|
-
- [Usage](docs/usage.md)
|
|
195
|
-
|
|
196
196
|
## Development
|
|
197
197
|
|
|
198
198
|
Contributor guidance -- setup, build and test commands, code style, the
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "soliplex"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.76"
|
|
8
8
|
description = "An AI-powered Retrieval-Augmented Generation (RAG) system with a modern web interface."
|
|
9
9
|
authors = [{ name = "Enfold", email = "info@enfoldsystems.net" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -19,13 +19,13 @@ dependencies = [
|
|
|
19
19
|
"aiosqlite",
|
|
20
20
|
"alembic >= 1.18.4, < 1.19",
|
|
21
21
|
"authlib",
|
|
22
|
-
"bubble-sandbox >= 0.
|
|
22
|
+
"bubble-sandbox >= 0.11.0, < 0.12",
|
|
23
23
|
"cryptography >= 50.0.0", # PYSEC-2026-3552
|
|
24
24
|
"fastapi",
|
|
25
25
|
"fastmcp >= 3.3.0, < 3.4",
|
|
26
26
|
"fakeredis != 2.35.0", # brownbag
|
|
27
27
|
"greenlet",
|
|
28
|
-
"haiku.rag-slim >= 0.
|
|
28
|
+
"haiku.rag-slim >= 0.74.0, < 0.75",
|
|
29
29
|
"idna >= 3.15",
|
|
30
30
|
"itsdangerous",
|
|
31
31
|
"joserfc >= 1.6.7, < 1.7", # CVE-2026-48990
|
|
@@ -4,8 +4,10 @@ import dataclasses
|
|
|
4
4
|
import typing
|
|
5
5
|
|
|
6
6
|
import pydantic_ai
|
|
7
|
-
from haiku.rag.capabilities import
|
|
8
|
-
from haiku.rag.capabilities import
|
|
7
|
+
from haiku.rag.capabilities import analysis as hr_caps_analysis
|
|
8
|
+
from haiku.rag.capabilities import compaction as hr_caps_compaction
|
|
9
|
+
from haiku.rag.capabilities import policy as hr_caps_policy
|
|
10
|
+
from haiku.rag.capabilities import rag as hr_caps_rag
|
|
9
11
|
from pydantic_ai import agent as ai_agent
|
|
10
12
|
from pydantic_ai import capabilities as ai_capabilities
|
|
11
13
|
from pydantic_ai import tools as ai_tools
|
|
@@ -20,6 +22,18 @@ from soliplex.config import tools as config_tools
|
|
|
20
22
|
|
|
21
23
|
ToolConfigMap = dict[str, typing.Any]
|
|
22
24
|
|
|
25
|
+
# Capabilities which take 'vision' from agent config's 'multimodal'
|
|
26
|
+
HR_VisionCapabilities = (
|
|
27
|
+
hr_caps_rag.RAGCapability | hr_caps_analysis.AnalysisCapability
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Capabilities which can always be eagerly loaded
|
|
31
|
+
HookOnlyCapabilities = (
|
|
32
|
+
hr_caps_compaction.EvidenceCompactionCapability
|
|
33
|
+
| hr_caps_policy.CitationPolicyCapability
|
|
34
|
+
| cap_rag_audit.RAGAccessAuditCapability
|
|
35
|
+
)
|
|
36
|
+
|
|
23
37
|
|
|
24
38
|
class CapabilityConfig(typing.Protocol):
|
|
25
39
|
"""Room-specific native capabilities and their audit targets."""
|
|
@@ -114,16 +128,20 @@ def get_default_agent_from_configs(
|
|
|
114
128
|
# agent's model, not haiku.rag's configured model, so gate on the room
|
|
115
129
|
# agent's declared multimodality.
|
|
116
130
|
for capability in capabilities:
|
|
117
|
-
if isinstance(capability,
|
|
131
|
+
if isinstance(capability, HR_VisionCapabilities):
|
|
118
132
|
capability.vision = agent_config.multimodal
|
|
119
133
|
|
|
120
134
|
# A single model-facing capability loads eagerly so its tools are visible;
|
|
121
|
-
#
|
|
122
|
-
#
|
|
135
|
+
#
|
|
136
|
+
# Multiple stay deferred so the model routes between them via
|
|
137
|
+
# 'load_capability'.
|
|
138
|
+
#
|
|
139
|
+
# Hook-only capabilities expose no tools to route
|
|
140
|
+
# between, so they neither defer nor count.
|
|
123
141
|
routing_capabilities = [
|
|
124
142
|
capability
|
|
125
143
|
for capability in capabilities
|
|
126
|
-
if not isinstance(capability,
|
|
144
|
+
if not isinstance(capability, HookOnlyCapabilities)
|
|
127
145
|
]
|
|
128
146
|
defer_loading = len(routing_capabilities) > 1
|
|
129
147
|
for capability in routing_capabilities:
|
|
@@ -5,6 +5,7 @@ import enum
|
|
|
5
5
|
import functools
|
|
6
6
|
import pathlib
|
|
7
7
|
import typing
|
|
8
|
+
import warnings
|
|
8
9
|
from collections import abc
|
|
9
10
|
|
|
10
11
|
from pydantic_ai import capabilities as ai_capabilities
|
|
@@ -50,13 +51,29 @@ class UnknownCapability(KeyError):
|
|
|
50
51
|
def __init__(self, name, _config_path=None):
|
|
51
52
|
self.name = name
|
|
52
53
|
self._config_path = _config_path
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
|
|
55
|
+
super().__init__(
|
|
56
|
+
f"Unknown capability name: {name} (configured in {_config_path})"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class UnknownAgentConfigKind(KeyError):
|
|
61
|
+
def __init__(self, kind, _config_path=None):
|
|
62
|
+
self.kind = kind
|
|
63
|
+
self._config_path = _config_path
|
|
64
|
+
super().__init__(
|
|
65
|
+
f"Unknown agent config kind: {kind} (configured in {_config_path})"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AgentConfigKindMismatch(ValueError):
|
|
70
|
+
def __init__(self, found_kind, expected_kind):
|
|
71
|
+
self.found_kind = found_kind
|
|
72
|
+
self.expected_kind = expected_kind
|
|
73
|
+
super().__init__(
|
|
74
|
+
"Agent config kind mismatch: "
|
|
75
|
+
f"found '{found_kind}', expected '{expected_kind}'"
|
|
76
|
+
)
|
|
60
77
|
|
|
61
78
|
|
|
62
79
|
class LLMProviderType(enum.StrEnum):
|
|
@@ -97,6 +114,13 @@ def _apply_agent_config_template(
|
|
|
97
114
|
return config_dict
|
|
98
115
|
|
|
99
116
|
|
|
117
|
+
ACC_BBB_NO_NAME_KEY_DEPRECATED = """\
|
|
118
|
+
AgentCapabilityConfig.from_yaml: '{<name>: <kwargs>}' form is deprecated;
|
|
119
|
+
use '{"name": <name>, "kwargs": <kwargs>}' instead. Support for the form
|
|
120
|
+
will be removed after 'v0.76'.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
|
|
100
124
|
@dataclasses.dataclass(kw_only=True)
|
|
101
125
|
class AgentCapabilityConfig:
|
|
102
126
|
name: str
|
|
@@ -104,36 +128,47 @@ class AgentCapabilityConfig:
|
|
|
104
128
|
|
|
105
129
|
_config_path: pathlib.Path = None
|
|
106
130
|
|
|
131
|
+
@classmethod
|
|
132
|
+
def from_yaml(
|
|
133
|
+
cls,
|
|
134
|
+
config_path: pathlib.Path,
|
|
135
|
+
config_dict_or_str: str | dict,
|
|
136
|
+
):
|
|
137
|
+
if isinstance(config_dict_or_str, str):
|
|
138
|
+
name = config_dict_or_str
|
|
139
|
+
config_dict = {"name": name}
|
|
140
|
+
else:
|
|
141
|
+
if "name" not in config_dict_or_str: # BBB deprecated
|
|
142
|
+
warnings.warn(
|
|
143
|
+
ACC_BBB_NO_NAME_KEY_DEPRECATED,
|
|
144
|
+
DeprecationWarning,
|
|
145
|
+
stacklevel=2,
|
|
146
|
+
)
|
|
147
|
+
((name, kwargs),) = config_dict_or_str.items()
|
|
148
|
+
config_dict = {"name": name, "kwargs": kwargs}
|
|
149
|
+
else:
|
|
150
|
+
name = config_dict_or_str["name"]
|
|
151
|
+
config_dict = config_dict_or_str
|
|
152
|
+
|
|
153
|
+
if name not in AGENT_CAPABILITY_CLASSES_BY_NAME:
|
|
154
|
+
raise UnknownCapability(name, config_path)
|
|
155
|
+
|
|
156
|
+
return cls(**config_dict, _config_path=config_path)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def as_yaml(self) -> dict:
|
|
160
|
+
return {"name": self.name, "kwargs": self.kwargs}
|
|
161
|
+
|
|
107
162
|
@property
|
|
108
163
|
def as_capability(self) -> ai_capabilities.AbstractCapability:
|
|
109
164
|
try:
|
|
110
165
|
cap_klass = AGENT_CAPABILITY_CLASSES_BY_NAME[self.name]
|
|
111
166
|
except KeyError:
|
|
112
|
-
raise UnknownCapability(self.name) from None
|
|
167
|
+
raise UnknownCapability(self.name, self._config_path) from None
|
|
113
168
|
|
|
114
169
|
return cap_klass(**self.kwargs)
|
|
115
170
|
|
|
116
171
|
|
|
117
|
-
def extract_capability_config(
|
|
118
|
-
cap_config: str | dict[str, typing.Any],
|
|
119
|
-
_config_path: pathlib.Path,
|
|
120
|
-
) -> AgentCapabilityConfig:
|
|
121
|
-
if isinstance(cap_config, str):
|
|
122
|
-
name = cap_config
|
|
123
|
-
kwargs = {}
|
|
124
|
-
else:
|
|
125
|
-
((name, kwargs),) = cap_config.items()
|
|
126
|
-
|
|
127
|
-
if name not in AGENT_CAPABILITY_CLASSES_BY_NAME:
|
|
128
|
-
raise UnknownCapability(name, _config_path)
|
|
129
|
-
|
|
130
|
-
return AgentCapabilityConfig(
|
|
131
|
-
name=name,
|
|
132
|
-
kwargs=kwargs,
|
|
133
|
-
_config_path=_config_path,
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
137
172
|
@dataclasses.dataclass(kw_only=True)
|
|
138
173
|
class AgentConfig:
|
|
139
174
|
#
|
|
@@ -177,6 +212,11 @@ class AgentConfig:
|
|
|
177
212
|
if system_prompt is not None:
|
|
178
213
|
self._system_prompt_text = system_prompt
|
|
179
214
|
|
|
215
|
+
@classmethod
|
|
216
|
+
def _check_kind(cls, kind):
|
|
217
|
+
if kind not in (None, cls.kind):
|
|
218
|
+
raise AgentConfigKindMismatch(kind, cls.kind)
|
|
219
|
+
|
|
180
220
|
@classmethod
|
|
181
221
|
def from_yaml(
|
|
182
222
|
cls,
|
|
@@ -185,6 +225,9 @@ class AgentConfig:
|
|
|
185
225
|
config_dict: dict,
|
|
186
226
|
):
|
|
187
227
|
try:
|
|
228
|
+
kind = config_dict.pop("kind", None)
|
|
229
|
+
cls._check_kind(kind)
|
|
230
|
+
|
|
188
231
|
config_dict["_installation_config"] = installation_config
|
|
189
232
|
config_dict["_config_path"] = config_path
|
|
190
233
|
|
|
@@ -210,7 +253,7 @@ class AgentConfig:
|
|
|
210
253
|
capabilities = config_dict.pop("capabilities", None)
|
|
211
254
|
if capabilities is not None:
|
|
212
255
|
config_dict["_capability_configs"] = [
|
|
213
|
-
|
|
256
|
+
AgentCapabilityConfig.from_yaml(config_path, cap)
|
|
214
257
|
for cap in capabilities
|
|
215
258
|
]
|
|
216
259
|
|
|
@@ -282,24 +325,25 @@ class AgentConfig:
|
|
|
282
325
|
if self._system_prompt_text is None
|
|
283
326
|
else self._system_prompt_text
|
|
284
327
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
provider_base_url = self.provider_base_url
|
|
328
|
+
capabilities = {}
|
|
329
|
+
|
|
330
|
+
for cap_cfg in self._capability_configs:
|
|
331
|
+
cap_list = capabilities.setdefault("capabilities", [])
|
|
332
|
+
cap_list.append(cap_cfg.as_yaml)
|
|
291
333
|
|
|
292
334
|
return {
|
|
293
335
|
"id": self.id,
|
|
336
|
+
"kind": self.kind,
|
|
294
337
|
"model_name": self.model_name,
|
|
295
338
|
"retries": self.retries,
|
|
296
339
|
"system_prompt": prompt,
|
|
297
340
|
"model_settings": self.model_settings,
|
|
298
341
|
"multimodal": self.multimodal,
|
|
299
342
|
"provider_type": str(self.provider_type),
|
|
300
|
-
"provider_base_url": provider_base_url,
|
|
343
|
+
"provider_base_url": self.provider_base_url,
|
|
301
344
|
"provider_key": self.provider_key, # "secret:SECRET_NAME"
|
|
302
|
-
|
|
345
|
+
"agui_feature_names": self.agui_feature_names,
|
|
346
|
+
} | capabilities
|
|
303
347
|
|
|
304
348
|
|
|
305
349
|
AgentFactory = abc.Callable[[], ai_ag_abstract.AbstractAgent]
|
|
@@ -342,6 +386,11 @@ class FactoryAgentConfig:
|
|
|
342
386
|
|
|
343
387
|
return self._factory
|
|
344
388
|
|
|
389
|
+
@classmethod
|
|
390
|
+
def _check_kind(cls, kind):
|
|
391
|
+
if kind not in (None, cls.kind):
|
|
392
|
+
raise AgentConfigKindMismatch(kind, cls.kind)
|
|
393
|
+
|
|
345
394
|
@classmethod
|
|
346
395
|
def from_yaml(
|
|
347
396
|
cls,
|
|
@@ -350,6 +399,9 @@ class FactoryAgentConfig:
|
|
|
350
399
|
config_dict: dict,
|
|
351
400
|
):
|
|
352
401
|
try:
|
|
402
|
+
kind = config_dict.pop("kind", None)
|
|
403
|
+
cls._check_kind(kind)
|
|
404
|
+
|
|
353
405
|
config_dict["_installation_config"] = installation_config
|
|
354
406
|
config_dict["_config_path"] = config_path
|
|
355
407
|
|
|
@@ -379,6 +431,7 @@ class FactoryAgentConfig:
|
|
|
379
431
|
"factory_name": self.factory_name,
|
|
380
432
|
"with_agent_config": self.with_agent_config,
|
|
381
433
|
"extra_config": self.extra_config,
|
|
434
|
+
"agui_feature_names": self.agui_feature_names,
|
|
382
435
|
}
|
|
383
436
|
|
|
384
437
|
|
|
@@ -400,14 +453,17 @@ def extract_agent_config(
|
|
|
400
453
|
config_path: pathlib.Path,
|
|
401
454
|
config_dict: dict,
|
|
402
455
|
) -> AgentConfig: # or subclass
|
|
403
|
-
agent_kind = config_dict.get("kind")
|
|
404
456
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
key: value for key, value in config_dict.items() if key != "kind"
|
|
408
|
-
}
|
|
457
|
+
# YAML not required to specify 'kind'
|
|
458
|
+
agent_kind = config_dict.get("kind", AgentConfig.kind)
|
|
409
459
|
|
|
410
|
-
|
|
460
|
+
try:
|
|
461
|
+
ac_class = AGENT_CONFIG_CLASSES_BY_KIND[agent_kind]
|
|
462
|
+
except KeyError:
|
|
463
|
+
raise UnknownAgentConfigKind(
|
|
464
|
+
agent_kind,
|
|
465
|
+
config_path,
|
|
466
|
+
) from None
|
|
411
467
|
|
|
412
468
|
return ac_class.from_yaml(
|
|
413
469
|
installation_config,
|
|
@@ -857,13 +857,14 @@ class InstallationConfig:
|
|
|
857
857
|
for mwc_yaml in config_dict.get("middleware_stack", ())
|
|
858
858
|
]
|
|
859
859
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
860
|
+
aro_klasses_by_kind = config_routing.APP_ROUTER_OPERATIONS_BY_KIND
|
|
861
|
+
aros, aros_cfg = config_dict.pop("app_router_operations", []), []
|
|
862
|
+
|
|
863
|
+
for aro in aros:
|
|
864
|
+
klass = aro_klasses_by_kind[aro["kind"]]
|
|
865
|
+
aros_cfg.append(klass.from_yaml(config_path, aro))
|
|
866
|
+
|
|
867
|
+
config_dict["app_router_operations"] = aros_cfg
|
|
867
868
|
|
|
868
869
|
secret_configs = [
|
|
869
870
|
config_secrets.SecretConfig.from_yaml(
|
|
@@ -1067,6 +1068,8 @@ class InstallationConfig:
|
|
|
1067
1068
|
def as_yaml(self) -> dict:
|
|
1068
1069
|
result = {
|
|
1069
1070
|
"id": self.id,
|
|
1071
|
+
"server_name": self.server_name,
|
|
1072
|
+
"server_description": self.server_description,
|
|
1070
1073
|
"meta": self.meta.as_yaml,
|
|
1071
1074
|
"secrets": [secret.as_yaml for secret in self.secrets],
|
|
1072
1075
|
# Dump the resolved version, not the original config
|
|
@@ -1076,13 +1079,31 @@ class InstallationConfig:
|
|
|
1076
1079
|
"filesystem_skills_paths": [
|
|
1077
1080
|
str(path) for path in self.filesystem_skills_paths
|
|
1078
1081
|
],
|
|
1079
|
-
"logging_config_file": str(self._logging_config_file),
|
|
1080
1082
|
"oidc_paths": [str(path) for path in self.oidc_paths],
|
|
1081
1083
|
"room_paths": [str(path) for path in self.room_paths],
|
|
1082
1084
|
"completion_paths": [str(path) for path in self.completion_paths],
|
|
1083
1085
|
"quizzes_paths": [str(path) for path in self.quizzes_paths],
|
|
1086
|
+
"thread_persistence_dburi": {
|
|
1087
|
+
"sync": self._thread_persistence_dburi_sync,
|
|
1088
|
+
"async": self._thread_persistence_dburi_async,
|
|
1089
|
+
},
|
|
1090
|
+
"authorization_dburi": {
|
|
1091
|
+
"sync": self._authorization_dburi_sync,
|
|
1092
|
+
"async": self._authorization_dburi_async,
|
|
1093
|
+
},
|
|
1084
1094
|
}
|
|
1085
1095
|
|
|
1096
|
+
if self.disable_dotenv:
|
|
1097
|
+
result["disable_dotenv"] = True
|
|
1098
|
+
|
|
1099
|
+
if self.middleware_stack:
|
|
1100
|
+
result["middleware_stack"] = [
|
|
1101
|
+
layer.as_yaml for layer in self.middleware_stack
|
|
1102
|
+
]
|
|
1103
|
+
|
|
1104
|
+
if self._skill_configs is not None:
|
|
1105
|
+
result["skill_configs"] = self._skill_configs
|
|
1106
|
+
|
|
1086
1107
|
if self.rooms_upload_path:
|
|
1087
1108
|
result["rooms_upload_path"] = str(self.rooms_upload_path)
|
|
1088
1109
|
|
|
@@ -1098,6 +1119,15 @@ class InstallationConfig:
|
|
|
1098
1119
|
if self.logfire_config is not None:
|
|
1099
1120
|
result["logfire_config"] = self.logfire_config.as_yaml
|
|
1100
1121
|
|
|
1122
|
+
if self._logging_config_file is not None:
|
|
1123
|
+
result["logging_config_file"] = str(self._logging_config_file)
|
|
1124
|
+
|
|
1125
|
+
if self._logging_headers_map is not None:
|
|
1126
|
+
result["logging_headers_map"] = self.logging_headers_map
|
|
1127
|
+
|
|
1128
|
+
if self._logging_claims_map is not None:
|
|
1129
|
+
result["logging_claims_map"] = self.logging_claims_map
|
|
1130
|
+
|
|
1101
1131
|
if self.app_router_operations:
|
|
1102
1132
|
result["app_router_operations"] = [
|
|
1103
1133
|
aro.as_yaml for aro in self.app_router_operations
|