soliplex 0.75__tar.gz → 0.75.2__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/src/soliplex.egg-info → soliplex-0.75.2}/PKG-INFO +3 -3
- {soliplex-0.75 → soliplex-0.75.2}/pyproject.toml +3 -3
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/capabilities/filesystem.py +83 -30
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/audit.py +1 -2
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/installation.py +19 -5
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/examples.py +7 -2
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tools/agui_run_feedback.py +53 -67
- {soliplex-0.75 → soliplex-0.75.2/src/soliplex.egg-info}/PKG-INFO +3 -3
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex.egg-info/requires.txt +2 -2
- {soliplex-0.75 → soliplex-0.75.2}/LICENSE +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/MANIFEST.in +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/README.md +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/example/skills/bare-bones/SKILL.md +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/example/skills/stanzas/SKILL.md +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/setup.cfg +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/skills/soliplex-docs/SKILL.md +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agents.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agui/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agui/parser.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agui/persistence.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agui/schema.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/agui/util.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/authn.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/authz/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/authz/persistence.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/authz/schema.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/capabilities/rag_audit.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/admin_users.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/ask.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/cli_util.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/main.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/ollama.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/room_authz.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/serve.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/cli/types.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/completions.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/_utils.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/agents.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/agui.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/authsystem.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/completions.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/exceptions.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/logfire.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/meta.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/middleware.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/quizzes.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/rag.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/rooms.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/routing.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/secrets.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/skills.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/config/tools.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/installation.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/log_ingest.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/loggers.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/main.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/mcp_auth.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/mcp_client.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/mcp_server.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/models.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/ollama.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/quizzes.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/rag_audit.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/sandbox_audit.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/secrets.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/skills/bwrap_sandbox/SKILL.md +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/skills/bwrap_sandbox/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/titles.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tools/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tools/file_uploads.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tools/rag.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tui/cli.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tui/main.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tui/rest_api.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/tui/serve.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/util.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/__init__.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/agui.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/authn.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/authz.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/completions.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/installation.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/log_ingest.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/quizzes.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/room_file_uploads.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/rooms.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/sandbox_workdirs.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/stats.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/streaming.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex/views/thread_file_uploads.py +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex.egg-info/SOURCES.txt +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex.egg-info/dependency_links.txt +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/src/soliplex.egg-info/entry_points.txt +0 -0
- {soliplex-0.75 → soliplex-0.75.2}/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.75
|
|
3
|
+
Version: 0.75.2
|
|
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
|
|
@@ -14,12 +14,12 @@ Requires-Dist: aiosqlite
|
|
|
14
14
|
Requires-Dist: alembic<1.19,>=1.18.4
|
|
15
15
|
Requires-Dist: authlib
|
|
16
16
|
Requires-Dist: bubble-sandbox<0.11,>=0.10.0
|
|
17
|
-
Requires-Dist: cryptography>=
|
|
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.74,>=0.73.0
|
|
23
23
|
Requires-Dist: idna>=3.15
|
|
24
24
|
Requires-Dist: itsdangerous
|
|
25
25
|
Requires-Dist: joserfc<1.7,>=1.6.7
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "soliplex"
|
|
7
|
-
version = "0.75"
|
|
7
|
+
version = "0.75.2"
|
|
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"
|
|
@@ -20,12 +20,12 @@ dependencies = [
|
|
|
20
20
|
"alembic >= 1.18.4, < 1.19",
|
|
21
21
|
"authlib",
|
|
22
22
|
"bubble-sandbox >= 0.10.0, < 0.11",
|
|
23
|
-
"cryptography >=
|
|
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.73.0, < 0.74",
|
|
29
29
|
"idna >= 3.15",
|
|
30
30
|
"itsdangerous",
|
|
31
31
|
"joserfc >= 1.6.7, < 1.7", # CVE-2026-48990
|
|
@@ -8,8 +8,8 @@ import typing
|
|
|
8
8
|
import anyio
|
|
9
9
|
import pydantic
|
|
10
10
|
import yaml
|
|
11
|
-
from pydantic_ai import ModelRetry
|
|
12
11
|
from pydantic_ai import capabilities as ai_capabilities
|
|
12
|
+
from pydantic_ai import exceptions as ai_exc
|
|
13
13
|
from pydantic_ai import toolsets as ai_toolsets
|
|
14
14
|
|
|
15
15
|
_SKILL_FILENAME = "SKILL.md"
|
|
@@ -28,6 +28,73 @@ class FilesystemCapabilityError(ValueError):
|
|
|
28
28
|
super().__init__(message)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class No_YAML_Frontmatter(FilesystemCapabilityError):
|
|
32
|
+
def __init__(self, path: pathlib.Path):
|
|
33
|
+
super().__init__(
|
|
34
|
+
f"{_SKILL_FILENAME} must start with YAML frontmatter",
|
|
35
|
+
path,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Invalid_YAML_Frontmatter(FilesystemCapabilityError):
|
|
40
|
+
def __init__(self, path: pathlib.Path):
|
|
41
|
+
super().__init__(
|
|
42
|
+
f"{_SKILL_FILENAME} has no closing frontmatter delimiter",
|
|
43
|
+
path,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CapabilityNameMismatch(FilesystemCapabilityError):
|
|
48
|
+
def __init__(self, skill_path: pathlib.Path, cap_name: str):
|
|
49
|
+
self.cap_name = cap_name
|
|
50
|
+
super().__init__(
|
|
51
|
+
f"Capability name '{cap_name}' does not match directory "
|
|
52
|
+
f"name '{skill_path.name}'",
|
|
53
|
+
skill_path,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SkillHasNoInstructions(FilesystemCapabilityError):
|
|
58
|
+
def __init__(self, skill_path: pathlib.Path):
|
|
59
|
+
super().__init__(
|
|
60
|
+
f"{_SKILL_FILENAME} contains no instructions",
|
|
61
|
+
skill_path,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class InvalidCapabilityName(ValueError):
|
|
66
|
+
def __init__(self, name):
|
|
67
|
+
self.name = name
|
|
68
|
+
super().__init__(
|
|
69
|
+
"name must contain lowercase letters, numbers, and single "
|
|
70
|
+
"hyphens only"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class MissingCapabilityFile(ai_exc.ModelRetry):
|
|
75
|
+
def __init__(self, dirname, filename):
|
|
76
|
+
self.dirname = dirname
|
|
77
|
+
self.filename = filename
|
|
78
|
+
super().__init__(f"'{filename}' is not under '{dirname}/'")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class NoSuchResource(ai_exc.ModelRetry):
|
|
82
|
+
def __init__(self, path):
|
|
83
|
+
self.path = path
|
|
84
|
+
super().__init__(f"No such resource: '{path}'")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class NoSuchScript(ai_exc.ModelRetry):
|
|
88
|
+
def __init__(self, path):
|
|
89
|
+
self.path = path
|
|
90
|
+
super().__init__(f"No such script: '{path}'")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class MalformedScriptArguments(ai_exc.ModelRetry):
|
|
94
|
+
def __init__(self, exc):
|
|
95
|
+
super().__init__(f"Malformed arguments: {exc}")
|
|
96
|
+
|
|
97
|
+
|
|
31
98
|
class _CapabilityMetadata(pydantic.BaseModel):
|
|
32
99
|
model_config = pydantic.ConfigDict(extra="ignore")
|
|
33
100
|
|
|
@@ -41,10 +108,8 @@ class _CapabilityMetadata(pydantic.BaseModel):
|
|
|
41
108
|
@classmethod
|
|
42
109
|
def validate_name(cls, value: str) -> str:
|
|
43
110
|
if not _NAME_PATTERN.fullmatch(value):
|
|
44
|
-
raise
|
|
45
|
-
|
|
46
|
-
"hyphens only"
|
|
47
|
-
)
|
|
111
|
+
raise InvalidCapabilityName(value)
|
|
112
|
+
|
|
48
113
|
return value
|
|
49
114
|
|
|
50
115
|
|
|
@@ -76,10 +141,10 @@ class FilesystemCapability(ai_capabilities.AbstractCapability[typing.Any]):
|
|
|
76
141
|
def _confined(self, dirname: str, value: str) -> pathlib.Path:
|
|
77
142
|
root = (self.path / dirname).resolve()
|
|
78
143
|
target = (self.path / value).resolve()
|
|
144
|
+
|
|
79
145
|
if not target.is_relative_to(root):
|
|
80
|
-
raise
|
|
81
|
-
|
|
82
|
-
)
|
|
146
|
+
raise MissingCapabilityFile(dirname, value)
|
|
147
|
+
|
|
83
148
|
return target
|
|
84
149
|
|
|
85
150
|
async def read_resource(self, path: str) -> str:
|
|
@@ -89,8 +154,10 @@ class FilesystemCapability(ai_capabilities.AbstractCapability[typing.Any]):
|
|
|
89
154
|
below ``resources/``, e.g. ``resources/jabberwocky.md``.
|
|
90
155
|
"""
|
|
91
156
|
target = self._confined(_RESOURCES_DIRNAME, path)
|
|
157
|
+
|
|
92
158
|
if not target.is_file():
|
|
93
|
-
raise
|
|
159
|
+
raise NoSuchResource(path)
|
|
160
|
+
|
|
94
161
|
return target.read_text(encoding="utf-8")
|
|
95
162
|
|
|
96
163
|
async def run_script(self, script: str, arguments: str = "") -> str:
|
|
@@ -101,14 +168,13 @@ class FilesystemCapability(ai_capabilities.AbstractCapability[typing.Any]):
|
|
|
101
168
|
single shell-style string of command-line arguments.
|
|
102
169
|
"""
|
|
103
170
|
target = self._confined(_SCRIPTS_DIRNAME, script)
|
|
171
|
+
|
|
104
172
|
if not target.is_file():
|
|
105
|
-
raise
|
|
173
|
+
raise NoSuchScript(script)
|
|
106
174
|
try:
|
|
107
175
|
argv = shlex.split(arguments)
|
|
108
176
|
except ValueError as exc:
|
|
109
|
-
raise
|
|
110
|
-
f"Malformed arguments: {exc}"
|
|
111
|
-
) from exc
|
|
177
|
+
raise MalformedScriptArguments(exc) from exc
|
|
112
178
|
|
|
113
179
|
try:
|
|
114
180
|
with anyio.fail_after(_SCRIPT_TIMEOUT_SECONDS):
|
|
@@ -142,10 +208,7 @@ def _parse_skill_file(
|
|
|
142
208
|
|
|
143
209
|
lines = text.splitlines()
|
|
144
210
|
if not lines or lines[0].strip() != _FRONTMATTER_DELIMITER:
|
|
145
|
-
raise
|
|
146
|
-
f"{_SKILL_FILENAME} must start with YAML frontmatter",
|
|
147
|
-
skill_path,
|
|
148
|
-
)
|
|
211
|
+
raise No_YAML_Frontmatter(skill_path)
|
|
149
212
|
|
|
150
213
|
try:
|
|
151
214
|
end = next(
|
|
@@ -154,10 +217,7 @@ def _parse_skill_file(
|
|
|
154
217
|
if line.strip() == _FRONTMATTER_DELIMITER
|
|
155
218
|
)
|
|
156
219
|
except StopIteration as exc:
|
|
157
|
-
raise
|
|
158
|
-
f"{_SKILL_FILENAME} has no closing frontmatter delimiter",
|
|
159
|
-
skill_path,
|
|
160
|
-
) from exc
|
|
220
|
+
raise Invalid_YAML_Frontmatter(skill_path) from exc
|
|
161
221
|
|
|
162
222
|
try:
|
|
163
223
|
metadata = _CapabilityMetadata.model_validate(
|
|
@@ -167,18 +227,11 @@ def _parse_skill_file(
|
|
|
167
227
|
raise FilesystemCapabilityError(str(exc), skill_path) from exc
|
|
168
228
|
|
|
169
229
|
if metadata.name != skill_path.name:
|
|
170
|
-
raise
|
|
171
|
-
f"Capability name '{metadata.name}' does not match directory "
|
|
172
|
-
f"name '{skill_path.name}'",
|
|
173
|
-
skill_path,
|
|
174
|
-
)
|
|
230
|
+
raise CapabilityNameMismatch(skill_path, cap_name=metadata.name)
|
|
175
231
|
|
|
176
232
|
instructions = "\n".join(lines[end + 1 :]).strip()
|
|
177
233
|
if not instructions:
|
|
178
|
-
raise
|
|
179
|
-
f"{_SKILL_FILENAME} contains no instructions",
|
|
180
|
-
skill_path,
|
|
181
|
-
)
|
|
234
|
+
raise SkillHasNoInstructions(skill_path)
|
|
182
235
|
|
|
183
236
|
return FilesystemCapability(
|
|
184
237
|
id=metadata.name,
|
|
@@ -241,8 +241,7 @@ def _missing_env_vars(the_installation: installation.Installation) -> dict:
|
|
|
241
241
|
try:
|
|
242
242
|
the_installation.resolve_environment()
|
|
243
243
|
except config_installation.MissingEnvVars as exc:
|
|
244
|
-
|
|
245
|
-
return {"missing_env_vars": missing}
|
|
244
|
+
return {"missing_env_vars": exc.failed}
|
|
246
245
|
return {}
|
|
247
246
|
|
|
248
247
|
|
|
@@ -58,13 +58,21 @@ class MissingEnvVar(ValueError):
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class MissingEnvVars(ExceptionGroup, ValueError):
|
|
61
|
+
failed = ()
|
|
62
|
+
|
|
61
63
|
def __init__(self, env_vars, excs):
|
|
62
|
-
self.env_vars = env_vars
|
|
63
64
|
super().__init__(
|
|
64
65
|
f"Environment variables cannot be resolved: {env_vars}",
|
|
65
66
|
excs,
|
|
66
67
|
)
|
|
67
68
|
|
|
69
|
+
@classmethod
|
|
70
|
+
def from_failed(cls, failed, excs):
|
|
71
|
+
msg = ",".join(failed)
|
|
72
|
+
inst = cls(msg, excs)
|
|
73
|
+
inst.failed = failed
|
|
74
|
+
return inst
|
|
75
|
+
|
|
68
76
|
|
|
69
77
|
class UnknownEnvironmentVariable(KeyError):
|
|
70
78
|
def __init__(self, env_var):
|
|
@@ -72,6 +80,12 @@ class UnknownEnvironmentVariable(KeyError):
|
|
|
72
80
|
super().__init__(f"Unknown environment variable '{env_var}'")
|
|
73
81
|
|
|
74
82
|
|
|
83
|
+
class UnsupportedInstallationSkillKind(ValueError):
|
|
84
|
+
def __init__(self, kind):
|
|
85
|
+
self.kind = kind
|
|
86
|
+
super().__init__(f"Unsupported installation skill kind: {kind}")
|
|
87
|
+
|
|
88
|
+
|
|
75
89
|
# ============================================================================
|
|
76
90
|
# Installation configuration types
|
|
77
91
|
# ============================================================================
|
|
@@ -211,9 +225,7 @@ def resolve_skill_configs(
|
|
|
211
225
|
|
|
212
226
|
for entry in explicit:
|
|
213
227
|
if entry["kind"] != config_skills.SkillKind.FILESYSTEM:
|
|
214
|
-
raise
|
|
215
|
-
f"Unsupported installation skill kind: {entry['kind']}"
|
|
216
|
-
)
|
|
228
|
+
raise UnsupportedInstallationSkillKind(entry["kind"])
|
|
217
229
|
skill_name = entry["skill_name"]
|
|
218
230
|
selected[skill_name] = available[skill_name]
|
|
219
231
|
|
|
@@ -508,7 +520,9 @@ class InstallationConfig:
|
|
|
508
520
|
failed.append(exc.env_var)
|
|
509
521
|
|
|
510
522
|
if excs:
|
|
511
|
-
|
|
523
|
+
# 'BaseExceeptionGroup.__new__' *requires* a 'str' for the
|
|
524
|
+
# first agument
|
|
525
|
+
raise MissingEnvVars.from_failed(failed, excs)
|
|
512
526
|
|
|
513
527
|
self.environment = {
|
|
514
528
|
key: resolve_file_prefix(value, self._config_path)
|
|
@@ -122,6 +122,11 @@ class _FauxRunResult:
|
|
|
122
122
|
return ai_usage.RunUsage()
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
class FailingOnRequest(ValueError):
|
|
126
|
+
def __init__(self):
|
|
127
|
+
super().__init__("failing on request")
|
|
128
|
+
|
|
129
|
+
|
|
125
130
|
@dataclasses.dataclass
|
|
126
131
|
class FauxAgent:
|
|
127
132
|
agent_config: config_agents.FactoryAgentConfig
|
|
@@ -158,7 +163,7 @@ class FauxAgent:
|
|
|
158
163
|
prompt = ups[-1].content
|
|
159
164
|
|
|
160
165
|
if prompt == "fail":
|
|
161
|
-
raise
|
|
166
|
+
raise FailingOnRequest()
|
|
162
167
|
|
|
163
168
|
return _FauxRunResult(output="I don't know!")
|
|
164
169
|
|
|
@@ -205,7 +210,7 @@ class FauxAgent:
|
|
|
205
210
|
),
|
|
206
211
|
)
|
|
207
212
|
if up.content == "fail":
|
|
208
|
-
raise
|
|
213
|
+
raise FailingOnRequest()
|
|
209
214
|
|
|
210
215
|
await asyncio.sleep(random.uniform(0.5, 2.0))
|
|
211
216
|
|
|
@@ -21,10 +21,13 @@ FRS = agui.FeedbackReviewStatus
|
|
|
21
21
|
STATE_NAMESPACE = "soliplex-agui-run-feedback"
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class UnknownFeedback(
|
|
24
|
+
class UnknownFeedback(pydantic_ai.ModelRetry):
|
|
25
25
|
def __init__(self, run_id: str):
|
|
26
26
|
self.run_id = run_id
|
|
27
|
-
super().__init__(
|
|
27
|
+
super().__init__(
|
|
28
|
+
f"Unknown feedback entry for run id {run_id}. "
|
|
29
|
+
"Call 'query_recent_feedback' and try again."
|
|
30
|
+
)
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
class RunFeedbackEntry(pydantic.BaseModel):
|
|
@@ -56,15 +59,15 @@ class RunFeedbackEntry(pydantic.BaseModel):
|
|
|
56
59
|
"""
|
|
57
60
|
|
|
58
61
|
user_name: str
|
|
59
|
-
email: str | None
|
|
62
|
+
email: str | None = None
|
|
60
63
|
room_id: str
|
|
61
64
|
thread_id: str
|
|
62
65
|
run_id: str
|
|
63
66
|
created: datetime.datetime
|
|
64
67
|
feedback: str
|
|
65
|
-
reason: str | None
|
|
66
|
-
status: agui.FeedbackReviewStatus | None
|
|
67
|
-
note: str | None
|
|
68
|
+
reason: str | None = None
|
|
69
|
+
status: agui.FeedbackReviewStatus | None = None
|
|
70
|
+
note: str | None = None
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
class RunFeedbackInfo(pydantic.BaseModel):
|
|
@@ -255,23 +258,20 @@ async def query_recent_feedback(
|
|
|
255
258
|
been reviewed or resolved.
|
|
256
259
|
"""
|
|
257
260
|
agui_state = ctx.deps.state
|
|
258
|
-
our_state =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
our_state = RecentRunFeedback()
|
|
263
|
-
else:
|
|
264
|
-
our_state = RecentRunFeedback.model_validate(our_state)
|
|
265
|
-
before_state = {STATE_NAMESPACE: our_state.model_dump(mode="json")}
|
|
261
|
+
our_state = RecentRunFeedback.model_validate(
|
|
262
|
+
agui_state.get(STATE_NAMESPACE) or {} # guard against actual 'None'
|
|
263
|
+
)
|
|
264
|
+
before_state = {STATE_NAMESPACE: our_state.model_dump(mode="json")}
|
|
266
265
|
|
|
267
266
|
entries = await _do_query(ctx, query)
|
|
268
267
|
our_state.query = query
|
|
269
268
|
our_state.entries = entries
|
|
270
269
|
|
|
271
|
-
|
|
270
|
+
after_json = our_state.model_dump(mode="json")
|
|
271
|
+
after_state = {STATE_NAMESPACE: after_json}
|
|
272
272
|
metadata = _response_metadata(before_state, after_state)
|
|
273
273
|
|
|
274
|
-
agui_state[STATE_NAMESPACE] =
|
|
274
|
+
agui_state[STATE_NAMESPACE] = after_json
|
|
275
275
|
|
|
276
276
|
return pydantic_ai.ToolReturn(our_state.entries, metadata=metadata)
|
|
277
277
|
|
|
@@ -314,9 +314,8 @@ async def get_feedback_run_info(
|
|
|
314
314
|
'run_id' is the UUID of the run.
|
|
315
315
|
"""
|
|
316
316
|
agui_state = ctx.deps.state
|
|
317
|
-
|
|
318
317
|
our_state = RecentRunFeedback.model_validate(
|
|
319
|
-
agui_state.get(STATE_NAMESPACE
|
|
318
|
+
agui_state.get(STATE_NAMESPACE) or {} # guard against actual 'None'
|
|
320
319
|
)
|
|
321
320
|
|
|
322
321
|
to_query, _ = _find_feedback_by_run_id(our_state, run_id)
|
|
@@ -365,73 +364,46 @@ async def get_feedback_run_info(
|
|
|
365
364
|
)
|
|
366
365
|
|
|
367
366
|
|
|
368
|
-
async def _do_review_feedback(
|
|
369
|
-
ctx: pydantic_ai.RunContext[agents.AgentDependencies],
|
|
370
|
-
run_entry: RunFeedbackEntry,
|
|
371
|
-
*,
|
|
372
|
-
note: str | None,
|
|
373
|
-
):
|
|
374
|
-
the_threads = ctx.deps.the_threads
|
|
375
|
-
user = ctx.deps.user
|
|
376
|
-
await the_threads.review_run_feedback(
|
|
377
|
-
reviewer_user_name=user.preferred_username,
|
|
378
|
-
reviewer_email=user.email,
|
|
379
|
-
note=note,
|
|
380
|
-
user_name=run_entry.user_name,
|
|
381
|
-
room_id=run_entry.room_id,
|
|
382
|
-
thread_id=run_entry.thread_id,
|
|
383
|
-
run_id=run_entry.run_id,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
|
|
387
367
|
async def review_recent_feedback(
|
|
388
368
|
ctx: pydantic_ai.RunContext[agents.AgentDependencies],
|
|
389
369
|
review: FeedbackReview,
|
|
390
370
|
) -> pydantic_ai.ToolReturn:
|
|
391
371
|
"""Add a user's review to a feedback entry for an AGUI run."""
|
|
392
372
|
agui_state = ctx.deps.state
|
|
393
|
-
to_review = None
|
|
394
|
-
|
|
395
373
|
our_state = RecentRunFeedback.model_validate(
|
|
396
|
-
agui_state.get(STATE_NAMESPACE
|
|
374
|
+
agui_state.get(STATE_NAMESPACE) or {} # guard against actual 'None'
|
|
397
375
|
)
|
|
398
376
|
|
|
399
377
|
to_review, _ = _find_feedback_by_run_id(
|
|
400
|
-
our_state, review.run_id, [
|
|
378
|
+
our_state, review.run_id, [FromWhichAttrs.OPENED]
|
|
401
379
|
)
|
|
402
380
|
|
|
403
381
|
before_state = {STATE_NAMESPACE: our_state.model_dump(mode="json")}
|
|
404
382
|
|
|
405
|
-
|
|
383
|
+
the_threads = ctx.deps.the_threads
|
|
384
|
+
user = ctx.deps.user
|
|
385
|
+
await the_threads.review_run_feedback(
|
|
386
|
+
reviewer_user_name=user.preferred_username,
|
|
387
|
+
reviewer_email=user.email,
|
|
388
|
+
note=review.note,
|
|
389
|
+
user_name=to_review.user_name,
|
|
390
|
+
room_id=to_review.room_id,
|
|
391
|
+
thread_id=to_review.thread_id,
|
|
392
|
+
run_id=to_review.run_id,
|
|
393
|
+
)
|
|
406
394
|
|
|
407
395
|
to_review.status = FRS.REVIEWED
|
|
408
396
|
to_review.note = review.note
|
|
409
397
|
our_state.entries.opened.remove(to_review)
|
|
410
398
|
our_state.entries.reviewed.insert(0, to_review)
|
|
411
399
|
|
|
412
|
-
|
|
400
|
+
after_json = our_state.model_dump(mode="json")
|
|
401
|
+
after_state = {STATE_NAMESPACE: after_json}
|
|
413
402
|
metadata = _response_metadata(before_state, after_state)
|
|
414
403
|
|
|
415
|
-
|
|
416
|
-
|
|
404
|
+
agui_state[STATE_NAMESPACE] = after_json
|
|
417
405
|
|
|
418
|
-
|
|
419
|
-
ctx: pydantic_ai.RunContext[agents.AgentDependencies],
|
|
420
|
-
run_entry: RunFeedbackEntry,
|
|
421
|
-
*,
|
|
422
|
-
note: str | None,
|
|
423
|
-
):
|
|
424
|
-
the_threads = ctx.deps.the_threads
|
|
425
|
-
user = ctx.deps.user
|
|
426
|
-
await the_threads.resolve_run_feedback(
|
|
427
|
-
resolver_user_name=user.preferred_username,
|
|
428
|
-
resolver_email=user.email,
|
|
429
|
-
note=note,
|
|
430
|
-
user_name=run_entry.user_name,
|
|
431
|
-
room_id=run_entry.room_id,
|
|
432
|
-
thread_id=run_entry.thread_id,
|
|
433
|
-
run_id=run_entry.run_id,
|
|
434
|
-
)
|
|
406
|
+
return pydantic_ai.ToolReturn(our_state.entries, metadata=metadata)
|
|
435
407
|
|
|
436
408
|
|
|
437
409
|
async def resolve_recent_feedback(
|
|
@@ -440,25 +412,39 @@ async def resolve_recent_feedback(
|
|
|
440
412
|
) -> pydantic_ai.ToolReturn:
|
|
441
413
|
"""Add a user's resolution of a feedback entry for an AGUI run."""
|
|
442
414
|
agui_state = ctx.deps.state
|
|
443
|
-
|
|
444
415
|
our_state = RecentRunFeedback.model_validate(
|
|
445
|
-
agui_state.get(STATE_NAMESPACE
|
|
416
|
+
agui_state.get(STATE_NAMESPACE) or {} # guard against actual 'None'
|
|
446
417
|
)
|
|
447
418
|
|
|
448
419
|
to_resolve, from_which = _find_feedback_by_run_id(
|
|
449
|
-
our_state,
|
|
420
|
+
our_state,
|
|
421
|
+
resolution.run_id,
|
|
422
|
+
[FromWhichAttrs.OPENED, FromWhichAttrs.REVIEWED],
|
|
450
423
|
)
|
|
451
424
|
|
|
452
425
|
before_state = {STATE_NAMESPACE: our_state.model_dump(mode="json")}
|
|
453
426
|
|
|
454
|
-
|
|
427
|
+
the_threads = ctx.deps.the_threads
|
|
428
|
+
user = ctx.deps.user
|
|
429
|
+
await the_threads.resolve_run_feedback(
|
|
430
|
+
resolver_user_name=user.preferred_username,
|
|
431
|
+
resolver_email=user.email,
|
|
432
|
+
note=resolution.note,
|
|
433
|
+
user_name=to_resolve.user_name,
|
|
434
|
+
room_id=to_resolve.room_id,
|
|
435
|
+
thread_id=to_resolve.thread_id,
|
|
436
|
+
run_id=to_resolve.run_id,
|
|
437
|
+
)
|
|
455
438
|
|
|
456
439
|
to_resolve.status = FRS.RESOLVED
|
|
457
440
|
to_resolve.note = resolution.note
|
|
458
441
|
from_which.remove(to_resolve)
|
|
459
442
|
our_state.entries.resolved.insert(0, to_resolve)
|
|
460
443
|
|
|
461
|
-
|
|
444
|
+
after_json = our_state.model_dump(mode="json")
|
|
445
|
+
after_state = {STATE_NAMESPACE: after_json}
|
|
462
446
|
metadata = _response_metadata(before_state, after_state)
|
|
463
447
|
|
|
448
|
+
agui_state[STATE_NAMESPACE] = after_json
|
|
449
|
+
|
|
464
450
|
return pydantic_ai.ToolReturn(our_state.entries, metadata=metadata)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soliplex
|
|
3
|
-
Version: 0.75
|
|
3
|
+
Version: 0.75.2
|
|
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
|
|
@@ -14,12 +14,12 @@ Requires-Dist: aiosqlite
|
|
|
14
14
|
Requires-Dist: alembic<1.19,>=1.18.4
|
|
15
15
|
Requires-Dist: authlib
|
|
16
16
|
Requires-Dist: bubble-sandbox<0.11,>=0.10.0
|
|
17
|
-
Requires-Dist: cryptography>=
|
|
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.74,>=0.73.0
|
|
23
23
|
Requires-Dist: idna>=3.15
|
|
24
24
|
Requires-Dist: itsdangerous
|
|
25
25
|
Requires-Dist: joserfc<1.7,>=1.6.7
|
|
@@ -4,12 +4,12 @@ aiosqlite
|
|
|
4
4
|
alembic<1.19,>=1.18.4
|
|
5
5
|
authlib
|
|
6
6
|
bubble-sandbox<0.11,>=0.10.0
|
|
7
|
-
cryptography>=
|
|
7
|
+
cryptography>=50.0.0
|
|
8
8
|
fastapi
|
|
9
9
|
fastmcp<3.4,>=3.3.0
|
|
10
10
|
fakeredis!=2.35.0
|
|
11
11
|
greenlet
|
|
12
|
-
haiku.rag-slim<0.
|
|
12
|
+
haiku.rag-slim<0.74,>=0.73.0
|
|
13
13
|
idna>=3.15
|
|
14
14
|
itsdangerous
|
|
15
15
|
joserfc<1.7,>=1.6.7
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|