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.
Files changed (80) hide show
  1. {gaard_api-0.2.16/src/gaard_api.egg-info → gaard_api-0.2.17}/PKG-INFO +5 -5
  2. {gaard_api-0.2.16 → gaard_api-0.2.17}/pyproject.toml +5 -5
  3. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/analysis.py +37 -1
  4. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/main.py +1 -1
  5. {gaard_api-0.2.16 → gaard_api-0.2.17/src/gaard_api.egg-info}/PKG-INFO +5 -5
  6. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/requires.txt +4 -4
  7. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_analysis_api.py +36 -0
  8. {gaard_api-0.2.16 → gaard_api-0.2.17}/MANIFEST.in +0 -0
  9. {gaard_api-0.2.16 → gaard_api-0.2.17}/README.md +0 -0
  10. {gaard_api-0.2.16 → gaard_api-0.2.17}/setup.cfg +0 -0
  11. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/__init__.py +0 -0
  12. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/__init__.py +0 -0
  13. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database.py +0 -0
  14. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_initial.sql +0 -0
  15. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_legacy_updates.sql +0 -0
  16. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/database_updates.sql +0 -0
  17. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/defaults.py +0 -0
  18. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/metadata.py +0 -0
  19. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/migration_runner.py +0 -0
  20. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/models.py +0 -0
  21. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/prompt_runtime.py +0 -0
  22. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/security.py +0 -0
  23. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin/services.py +0 -0
  24. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/getgaard.svg +0 -0
  25. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/identity.js +0 -0
  26. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/main.js +0 -0
  27. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/styles.css +0 -0
  28. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack-all.js +0 -0
  29. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack.min.css +0 -0
  30. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/index.html +0 -0
  31. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/admin-web/package.json +0 -0
  32. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/analysis_findings.py +0 -0
  33. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/__init__.py +0 -0
  34. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/__init__.py +0 -0
  35. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/admin.py +0 -0
  36. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/dashboards.py +0 -0
  37. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/prompts.py +0 -0
  38. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/query.py +0 -0
  39. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api/v1/schema.py +0 -0
  40. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/api_registry.py +0 -0
  41. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/auth_dependencies.py +0 -0
  42. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/auth_hooks.py +0 -0
  43. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/cli.py +0 -0
  44. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/cli_commands.py +0 -0
  45. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/conversations.py +0 -0
  46. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/__init__.py +0 -0
  47. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/error_handlers.py +0 -0
  48. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/schema_cache.py +0 -0
  49. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/core/settings.py +0 -0
  50. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/__init__.py +0 -0
  51. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
  52. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
  53. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_data/medical_poc/seed.sql +0 -0
  54. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/example_database.py +0 -0
  55. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/extension_services.py +0 -0
  56. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/extensions.py +0 -0
  57. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/license.py +0 -0
  58. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/package_updates.py +0 -0
  59. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/py.typed +0 -0
  60. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/query_hooks.py +0 -0
  61. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/server_cli.py +0 -0
  62. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/siem.py +0 -0
  63. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api/tls_http.py +0 -0
  64. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/SOURCES.txt +0 -0
  65. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/dependency_links.txt +0 -0
  66. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/entry_points.txt +0 -0
  67. {gaard_api-0.2.16 → gaard_api-0.2.17}/src/gaard_api.egg-info/top_level.txt +0 -0
  68. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_admin_api.py +0 -0
  69. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_api_cli.py +0 -0
  70. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_api_registry.py +0 -0
  71. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_conversation_api.py +0 -0
  72. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_database_migrations.py +0 -0
  73. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_error_handlers.py +0 -0
  74. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_license_api.py +0 -0
  75. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_license_service.py +0 -0
  76. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_prompt_runtime.py +0 -0
  77. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_query_error_messages.py +0 -0
  78. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_server_cli.py +0 -0
  79. {gaard_api-0.2.16 → gaard_api-0.2.17}/tests/test_settings.py +0 -0
  80. {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.16
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.11
8
- Requires-Dist: gaard-connectors==0.2.11
9
- Requires-Dist: gaard-llm==0.2.11
10
- Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.11
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.16"
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.11",
14
- "gaard-connectors==0.2.11",
15
- "gaard-llm==0.2.11",
16
- "gaard-plugin-api>=0.2.11,<0.3.0",
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.16",
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.16
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.11
8
- Requires-Dist: gaard-connectors==0.2.11
9
- Requires-Dist: gaard-llm==0.2.11
10
- Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.11
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.11
2
- gaard-connectors==0.2.11
3
- gaard-llm==0.2.11
4
- gaard-plugin-api<0.3.0,>=0.2.11
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