gaard-api 0.2.16__tar.gz → 0.2.17__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.
- {gaard_api-0.2.16/src/gaard_api.egg-info → gaard_api-0.2.17}/PKG-INFO +5 -5
- {gaard_api-0.2.16 → gaard_api-0.2.17}/pyproject.toml +5 -5
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/analysis.py +37 -1
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/main.py +1 -1
- {gaard_api-0.2.16 → gaard_api-0.2.17/src/gaard_api.egg-info}/PKG-INFO +5 -5
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/requires.txt +4 -4
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_analysis_api.py +36 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/MANIFEST.in +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/README.md +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/setup.cfg +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_initial.sql +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_legacy_updates.sql +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_updates.sql +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/defaults.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/metadata.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/migration_runner.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/models.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/prompt_runtime.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/security.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/services.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/getgaard.svg +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/identity.js +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/main.js +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/styles.css +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack-all.js +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack.min.css +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/index.html +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/package.json +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/analysis_findings.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/admin.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/dashboards.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/prompts.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/query.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/schema.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api_registry.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/auth_dependencies.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/auth_hooks.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/cli.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/cli_commands.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/conversations.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/error_handlers.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/schema_cache.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/settings.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/seed.sql +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_database.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/extension_services.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/extensions.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/license.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/package_updates.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/py.typed +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/query_hooks.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/server_cli.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/siem.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/tls_http.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/SOURCES.txt +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/dependency_links.txt +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/entry_points.txt +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/top_level.txt +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_admin_api.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_api_cli.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_api_registry.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_conversation_api.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_database_migrations.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_error_handlers.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_license_api.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_license_service.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_prompt_runtime.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_query_error_messages.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_server_cli.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_settings.py +0 -0
- {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_tls_http.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaard-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: GAARD backend web services providing admin interface
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: gaard-core==0.2.
|
|
8
|
-
Requires-Dist: gaard-connectors==0.2.
|
|
9
|
-
Requires-Dist: gaard-llm==0.2.
|
|
10
|
-
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.
|
|
7
|
+
Requires-Dist: gaard-core==0.2.17
|
|
8
|
+
Requires-Dist: gaard-connectors==0.2.17
|
|
9
|
+
Requires-Dist: gaard-llm==0.2.17
|
|
10
|
+
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.17
|
|
11
11
|
Requires-Dist: fastapi>=0.111.0
|
|
12
12
|
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
13
13
|
Requires-Dist: pydantic>=2.7.0
|
|
@@ -4,16 +4,16 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gaard-api"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.17"
|
|
8
8
|
description = "GAARD backend web services providing admin interface"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
|
|
12
12
|
dependencies = [
|
|
13
|
-
"gaard-core==0.2.
|
|
14
|
-
"gaard-connectors==0.2.
|
|
15
|
-
"gaard-llm==0.2.
|
|
16
|
-
"gaard-plugin-api>=0.2.
|
|
13
|
+
"gaard-core==0.2.17",
|
|
14
|
+
"gaard-connectors==0.2.17",
|
|
15
|
+
"gaard-llm==0.2.17",
|
|
16
|
+
"gaard-plugin-api>=0.2.17,<0.3.0",
|
|
17
17
|
"fastapi>=0.111.0",
|
|
18
18
|
"uvicorn[standard]>=0.30.0",
|
|
19
19
|
"pydantic>=2.7.0",
|
|
@@ -81,6 +81,27 @@ EVIDENCE_REFERENCE_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_.-]{0,63}:[^\s]{1,190}$
|
|
|
81
81
|
ACTIVE_WORKING_KNOWLEDGE_STATUSES = {"running", "waiting_for_user"}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
def normalize_planner_text_list(value: Any) -> list[str]:
|
|
85
|
+
if value is None:
|
|
86
|
+
return []
|
|
87
|
+
|
|
88
|
+
items = value if isinstance(value, list) else [value]
|
|
89
|
+
normalized: list[str] = []
|
|
90
|
+
seen: set[str] = set()
|
|
91
|
+
for item in items:
|
|
92
|
+
if item is None or isinstance(item, bool):
|
|
93
|
+
continue
|
|
94
|
+
if not isinstance(item, (str, int, float)):
|
|
95
|
+
continue
|
|
96
|
+
text = str(item).strip()
|
|
97
|
+
if not text or text in seen:
|
|
98
|
+
continue
|
|
99
|
+
seen.add(text)
|
|
100
|
+
normalized.append(text)
|
|
101
|
+
|
|
102
|
+
return normalized
|
|
103
|
+
|
|
104
|
+
|
|
84
105
|
class AnalysisAction(StrEnum):
|
|
85
106
|
ANSWER_FROM_CONTEXT = "answer_from_context"
|
|
86
107
|
ASK_USER = "ask_user"
|
|
@@ -101,6 +122,11 @@ class AnalysisBusinessLogicFinding(BaseModel):
|
|
|
101
122
|
terms: list[str] = Field(default_factory=list)
|
|
102
123
|
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
|
|
103
124
|
|
|
125
|
+
@field_validator("evidence_refs", "terms", mode="before")
|
|
126
|
+
@classmethod
|
|
127
|
+
def normalize_text_lists(cls, value: Any) -> list[str]:
|
|
128
|
+
return normalize_planner_text_list(value)
|
|
129
|
+
|
|
104
130
|
|
|
105
131
|
class AnalysisFindingEvidenceUpdate(BaseModel):
|
|
106
132
|
finding_id: str = Field(min_length=1, max_length=64)
|
|
@@ -109,6 +135,11 @@ class AnalysisFindingEvidenceUpdate(BaseModel):
|
|
|
109
135
|
summary: str = Field(min_length=1, max_length=4_000)
|
|
110
136
|
evidence_refs: list[str] = Field(default_factory=list, max_length=100)
|
|
111
137
|
|
|
138
|
+
@field_validator("evidence_refs", mode="before")
|
|
139
|
+
@classmethod
|
|
140
|
+
def normalize_evidence_refs(cls, value: Any) -> list[str]:
|
|
141
|
+
return normalize_planner_text_list(value)
|
|
142
|
+
|
|
112
143
|
|
|
113
144
|
class AnalysisFindingUsage(BaseModel):
|
|
114
145
|
finding_id: str = Field(min_length=1, max_length=64)
|
|
@@ -118,6 +149,11 @@ class AnalysisFindingUsage(BaseModel):
|
|
|
118
149
|
statement: str = Field(min_length=1, max_length=4_000)
|
|
119
150
|
evidence_refs: list[str] = Field(default_factory=list, max_length=100)
|
|
120
151
|
|
|
152
|
+
@field_validator("evidence_refs", mode="before")
|
|
153
|
+
@classmethod
|
|
154
|
+
def normalize_evidence_refs(cls, value: Any) -> list[str]:
|
|
155
|
+
return normalize_planner_text_list(value)
|
|
156
|
+
|
|
121
157
|
|
|
122
158
|
class AnalysisPlannerDecision(BaseModel):
|
|
123
159
|
action: AnalysisAction
|
|
@@ -1004,7 +1040,7 @@ Business logic learning:
|
|
|
1004
1040
|
- If the latest database observation reveals durable knowledge such as dictionary values, terminology meaning, table meaning, column meaning, or stable domain rules, include business_logic.create_suggestion=true.
|
|
1005
1041
|
- Distinct values, enumerations, statuses, categories, and table/column meaning discovered through ask_database should be saved as business logic findings unless they are clearly one-off metrics for the current answer.
|
|
1006
1042
|
- Do not create business logic for one-off answer values that are only useful for the current final answer.
|
|
1007
|
-
- statement must be a short, self-contained semantic claim. critique must state its main limitation or counterargument. Cite supporting query references in evidence_refs. Never include private chain-of-thought.
|
|
1043
|
+
- statement must be a short, self-contained semantic claim. critique must state its main limitation or counterargument. Cite supporting query references as JSON strings in evidence_refs; use the form "query:<audit_id>" for query audit references. Never include private chain-of-thought.
|
|
1008
1044
|
|
|
1009
1045
|
Investigation-scoped working knowledge:
|
|
1010
1046
|
- session_context.working_knowledge contains externally reviewed semantic evidence for this investigation only.
|
|
@@ -40,7 +40,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
|
40
40
|
|
|
41
41
|
app = FastAPI(
|
|
42
42
|
title="GAARD API",
|
|
43
|
-
version="0.2.
|
|
43
|
+
version="0.2.17",
|
|
44
44
|
description="Self-hosted AI SQL Gateway for governed natural-language access to relational data.",
|
|
45
45
|
lifespan=lifespan,
|
|
46
46
|
)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaard-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: GAARD backend web services providing admin interface
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: gaard-core==0.2.
|
|
8
|
-
Requires-Dist: gaard-connectors==0.2.
|
|
9
|
-
Requires-Dist: gaard-llm==0.2.
|
|
10
|
-
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.
|
|
7
|
+
Requires-Dist: gaard-core==0.2.17
|
|
8
|
+
Requires-Dist: gaard-connectors==0.2.17
|
|
9
|
+
Requires-Dist: gaard-llm==0.2.17
|
|
10
|
+
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.17
|
|
11
11
|
Requires-Dist: fastapi>=0.111.0
|
|
12
12
|
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
13
13
|
Requires-Dist: pydantic>=2.7.0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
gaard-core==0.2.
|
|
2
|
-
gaard-connectors==0.2.
|
|
3
|
-
gaard-llm==0.2.
|
|
4
|
-
gaard-plugin-api<0.3.0,>=0.2.
|
|
1
|
+
gaard-core==0.2.17
|
|
2
|
+
gaard-connectors==0.2.17
|
|
3
|
+
gaard-llm==0.2.17
|
|
4
|
+
gaard-plugin-api<0.3.0,>=0.2.17
|
|
5
5
|
fastapi>=0.111.0
|
|
6
6
|
uvicorn[standard]>=0.30.0
|
|
7
7
|
pydantic>=2.7.0
|
|
@@ -134,6 +134,42 @@ def create_active_default_datasource() -> DatasourceConnector:
|
|
|
134
134
|
return connector
|
|
135
135
|
|
|
136
136
|
|
|
137
|
+
def test_analysis_planner_normalizes_model_generated_text_lists() -> None:
|
|
138
|
+
decision = analysis_module.parse_analysis_decision(
|
|
139
|
+
json.dumps(
|
|
140
|
+
{
|
|
141
|
+
"action": "answer_from_context",
|
|
142
|
+
"business_logic": {
|
|
143
|
+
"evidence_refs": [156, " 157 ", 156, None, False, {"audit_id": 158}],
|
|
144
|
+
"terms": [" cardiology ", 23, "cardiology", None],
|
|
145
|
+
},
|
|
146
|
+
"finding_updates": [
|
|
147
|
+
{
|
|
148
|
+
"finding_id": "finding-1",
|
|
149
|
+
"effect": "strengthened",
|
|
150
|
+
"confidence": 0.9,
|
|
151
|
+
"summary": "Confirmed by another query.",
|
|
152
|
+
"evidence_refs": 158,
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"finding_usages": [
|
|
156
|
+
{
|
|
157
|
+
"finding_id": "finding-1",
|
|
158
|
+
"usage": "used_for_query",
|
|
159
|
+
"statement": "Used to constrain the query.",
|
|
160
|
+
"evidence_refs": "query:159",
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
assert decision.business_logic.evidence_refs == ["156", "157"]
|
|
168
|
+
assert decision.business_logic.terms == ["cardiology", "23"]
|
|
169
|
+
assert decision.finding_updates[0].evidence_refs == ["158"]
|
|
170
|
+
assert decision.finding_usages[0].evidence_refs == ["query:159"]
|
|
171
|
+
|
|
172
|
+
|
|
137
173
|
def test_analysis_stream_runs_final_query_and_persists_session(
|
|
138
174
|
analysis_client: TestClient,
|
|
139
175
|
) -> None:
|
|
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
|