gaard-api 0.1.3__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.
- {gaard_api-0.1.3/src/gaard_api.egg-info → gaard_api-0.2.0}/PKG-INFO +5 -5
- {gaard_api-0.1.3 → gaard_api-0.2.0}/pyproject.toml +5 -5
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/database.py +10 -3
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/defaults.py +0 -67
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/models.py +19 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/prompt_runtime.py +1 -46
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/services.py +132 -251
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin-web/assets/main.js +8 -9
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin-web/src/main.ts +8 -6
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api/v1/admin.py +29 -62
- gaard_api-0.2.0/src/gaard_api/api/v1/analysis.py +1242 -0
- gaard_api-0.2.0/src/gaard_api/api/v1/query.py +592 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/core/settings.py +11 -9
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/main.py +3 -1
- {gaard_api-0.1.3 → gaard_api-0.2.0/src/gaard_api.egg-info}/PKG-INFO +5 -5
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api.egg-info/SOURCES.txt +3 -1
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api.egg-info/requires.txt +4 -4
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_admin_api.py +69 -209
- gaard_api-0.2.0/tests/test_analysis_api.py +410 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_prompt_runtime.py +0 -31
- gaard_api-0.1.3/tests/test_investigation_error_messages.py → gaard_api-0.2.0/tests/test_query_error_messages.py +1 -1
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_settings.py +1 -4
- gaard_api-0.1.3/src/gaard_api/api/v1/query.py +0 -1113
- {gaard_api-0.1.3 → gaard_api-0.2.0}/MANIFEST.in +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/README.md +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/setup.cfg +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin/security.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin-web/assets/styles.css +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin-web/index.html +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/admin-web/package.json +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api/v1/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api/v1/prompts.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api/v1/schema.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/api_registry.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/cli.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/cli_commands.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/core/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/core/error_handlers.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/core/schema_cache.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/example_data/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/example_data/medical_poc/seed.sql +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/example_database.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/extension_services.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/extensions.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api/server_cli.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api.egg-info/dependency_links.txt +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api.egg-info/entry_points.txt +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/src/gaard_api.egg-info/top_level.txt +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_api_registry.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_error_handlers.py +0 -0
- {gaard_api-0.1.3 → gaard_api-0.2.0}/tests/test_server_cli.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaard-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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.
|
|
8
|
-
Requires-Dist: gaard-connectors==0.
|
|
9
|
-
Requires-Dist: gaard-llm==0.
|
|
10
|
-
Requires-Dist: gaard-plugin-api<0.
|
|
7
|
+
Requires-Dist: gaard-core==0.2.0
|
|
8
|
+
Requires-Dist: gaard-connectors==0.2.0
|
|
9
|
+
Requires-Dist: gaard-llm==0.2.0
|
|
10
|
+
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.0
|
|
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.
|
|
7
|
+
version = "0.2.0"
|
|
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.
|
|
14
|
-
"gaard-connectors==0.
|
|
15
|
-
"gaard-llm==0.
|
|
16
|
-
"gaard-plugin-api>=0.
|
|
13
|
+
"gaard-core==0.2.0",
|
|
14
|
+
"gaard-connectors==0.2.0",
|
|
15
|
+
"gaard-llm==0.2.0",
|
|
16
|
+
"gaard-plugin-api>=0.2.0,<0.3.0",
|
|
17
17
|
"fastapi>=0.111.0",
|
|
18
18
|
"uvicorn[standard]>=0.30.0",
|
|
19
19
|
"pydantic>=2.7.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Iterator
|
|
2
2
|
import json
|
|
3
3
|
|
|
4
|
-
from sqlalchemy import create_engine, select
|
|
4
|
+
from sqlalchemy import create_engine, delete, select
|
|
5
5
|
from sqlalchemy.engine import Engine
|
|
6
6
|
from sqlalchemy import inspect, text
|
|
7
7
|
from sqlalchemy.orm import Session, sessionmaker
|
|
@@ -24,6 +24,7 @@ from gaard_api.core.settings import settings
|
|
|
24
24
|
_engine: Engine | None = None
|
|
25
25
|
_session_factory: sessionmaker[Session] | None = None
|
|
26
26
|
_engine_url: str | None = None
|
|
27
|
+
LEGACY_PROMPT_KEYS = {"investigation_readiness"}
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def get_engine() -> Engine:
|
|
@@ -105,10 +106,12 @@ def seed_settings(session: Session) -> None:
|
|
|
105
106
|
"gaard_sql_generation_mode": settings.gaard_sql_generation_mode,
|
|
106
107
|
"gaard_result_interpretation_mode": settings.gaard_result_interpretation_mode,
|
|
107
108
|
"gaard_output_classification_mode": settings.gaard_output_classification_mode,
|
|
108
|
-
"gaard_investigation_mode": settings.gaard_investigation_mode,
|
|
109
|
-
"gaard_investigation_ambiguity_mode": settings.gaard_investigation_ambiguity_mode,
|
|
110
109
|
"gaard_query_max_rows": str(settings.gaard_query_max_rows),
|
|
111
110
|
"gaard_query_timeout_seconds": str(settings.gaard_query_timeout_seconds),
|
|
111
|
+
"gaard_analysis_loop_count": str(settings.gaard_analysis_loop_count),
|
|
112
|
+
"gaard_analysis_auto_enable_business_logic": (
|
|
113
|
+
"true" if settings.gaard_analysis_auto_enable_business_logic else "false"
|
|
114
|
+
),
|
|
112
115
|
"gaard_llm_provider": settings.gaard_llm_provider,
|
|
113
116
|
"gaard_llm_base_url": settings.gaard_llm_base_url,
|
|
114
117
|
"gaard_llm_api_key": settings.gaard_llm_api_key,
|
|
@@ -154,6 +157,10 @@ def apply_runtime_settings(session: Session) -> None:
|
|
|
154
157
|
|
|
155
158
|
|
|
156
159
|
def seed_prompts(session: Session) -> None:
|
|
160
|
+
session.execute(
|
|
161
|
+
delete(PromptTemplate).where(PromptTemplate.prompt_key.in_(LEGACY_PROMPT_KEYS))
|
|
162
|
+
)
|
|
163
|
+
|
|
157
164
|
for prompt in DEFAULT_PROMPTS:
|
|
158
165
|
existing = session.scalar(
|
|
159
166
|
select(PromptTemplate).where(PromptTemplate.prompt_key == prompt["prompt_key"])
|
|
@@ -86,66 +86,6 @@ Return one JSON object with:
|
|
|
86
86
|
- reason: short explanation
|
|
87
87
|
"""
|
|
88
88
|
|
|
89
|
-
DEFAULT_INVESTIGATION_READINESS_SYSTEM_PROMPT = """You are GAARD Investigation Readiness.
|
|
90
|
-
|
|
91
|
-
Your task is to decide whether GAARD already knows enough to create a correct SQL query for the user's question.
|
|
92
|
-
|
|
93
|
-
Assume nothing. Verify continuously.
|
|
94
|
-
|
|
95
|
-
Use only:
|
|
96
|
-
- the user's question,
|
|
97
|
-
- the active datasource schema,
|
|
98
|
-
- the approved or previously saved business logic supplied in the payload.
|
|
99
|
-
|
|
100
|
-
You do not generate SQL.
|
|
101
|
-
You do not answer the user.
|
|
102
|
-
You decide only whether normal SQL generation may start safely.
|
|
103
|
-
|
|
104
|
-
Return ready_for_sql=true only when all information needed for correct SQL is explicit in the question, schema, and business logic:
|
|
105
|
-
- requested business entity or metric,
|
|
106
|
-
- relevant tables, views and columns,
|
|
107
|
-
- required filters and dictionary/status values,
|
|
108
|
-
- required joins or relationships,
|
|
109
|
-
- requested output shape such as count, list, detail, or aggregation.
|
|
110
|
-
|
|
111
|
-
Return ready_for_sql=false when any material element is missing, ambiguous, inferred only from the model, or would require checking data values before SQL can be trusted. In that case route must be analysis.
|
|
112
|
-
|
|
113
|
-
Output rules:
|
|
114
|
-
- Return only a JSON object.
|
|
115
|
-
- Do not include markdown.
|
|
116
|
-
- Do not include reasoning outside the JSON.
|
|
117
|
-
- Do not include <think> blocks.
|
|
118
|
-
- Use exactly this JSON shape:
|
|
119
|
-
{"ready_for_sql":false,"route":"analysis","confidence":0.0,"reason":"short reason","missing_information":[],"required_analysis":[],"required_analysis_tasks":[],"assumptions":[]}
|
|
120
|
-
|
|
121
|
-
Required analysis task shape:
|
|
122
|
-
{"missing_information":"what is missing","required_analysis":"specific read-only data question for SQL analysis","category":"dictionary_value","expected_output":"what kind of result would resolve this"}
|
|
123
|
-
|
|
124
|
-
Allowed categories:
|
|
125
|
-
- dictionary_value
|
|
126
|
-
- relationship_logic
|
|
127
|
-
- filter_logic
|
|
128
|
-
- aggregation_logic
|
|
129
|
-
- entity_mapping
|
|
130
|
-
- unknown
|
|
131
|
-
"""
|
|
132
|
-
|
|
133
|
-
DEFAULT_INVESTIGATION_READINESS_USER_PROMPT = """Assess whether normal SQL generation can start.
|
|
134
|
-
|
|
135
|
-
Input JSON:
|
|
136
|
-
{payload}
|
|
137
|
-
|
|
138
|
-
Return one JSON object with:
|
|
139
|
-
- ready_for_sql: boolean
|
|
140
|
-
- route: sql or analysis
|
|
141
|
-
- confidence: number from 0 to 1
|
|
142
|
-
- reason: short explanation
|
|
143
|
-
- missing_information: list of missing or ambiguous items
|
|
144
|
-
- required_analysis: list of checks that Analysis mode should perform when ready_for_sql=false
|
|
145
|
-
- required_analysis_tasks: list of structured SQL-analysis tasks with missing_information, required_analysis, category, expected_output
|
|
146
|
-
- assumptions: list of any assumptions that would affect SQL correctness
|
|
147
|
-
"""
|
|
148
|
-
|
|
149
89
|
DEFAULT_RESULT_INTERPRETATION_SYSTEM_PROMPT = """You are GAARD Data Result Interpreter.
|
|
150
90
|
|
|
151
91
|
Your task is to explain SQL query results to the user.
|
|
@@ -247,13 +187,6 @@ DEFAULT_PROMPTS = [
|
|
|
247
187
|
"system_prompt": DEFAULT_SQL_GENERATION_SYSTEM_PROMPT,
|
|
248
188
|
"user_prompt_template": DEFAULT_SQL_GENERATION_USER_PROMPT,
|
|
249
189
|
},
|
|
250
|
-
{
|
|
251
|
-
"prompt_key": "investigation_readiness",
|
|
252
|
-
"name": "Investigation: readiness",
|
|
253
|
-
"description": "Decides whether Investigation can safely delegate to normal SQL generation.",
|
|
254
|
-
"system_prompt": DEFAULT_INVESTIGATION_READINESS_SYSTEM_PROMPT,
|
|
255
|
-
"user_prompt_template": DEFAULT_INVESTIGATION_READINESS_USER_PROMPT,
|
|
256
|
-
},
|
|
257
190
|
{
|
|
258
191
|
"prompt_key": "result_interpretation",
|
|
259
192
|
"name": "Result interpretation",
|
|
@@ -251,3 +251,22 @@ class BusinessKnowledgeClaim(Base):
|
|
|
251
251
|
onupdate=utc_now,
|
|
252
252
|
)
|
|
253
253
|
updated_by: Mapped[str] = mapped_column(String(255), default="system")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class AnalysisSessionRecord(Base):
|
|
257
|
+
__tablename__ = "analysis_sessions"
|
|
258
|
+
|
|
259
|
+
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
260
|
+
session_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
|
261
|
+
status: Mapped[str] = mapped_column(String(50), index=True, default="running")
|
|
262
|
+
user_id: Mapped[str] = mapped_column(String(255), index=True)
|
|
263
|
+
datasource_id: Mapped[str] = mapped_column(String(255), index=True)
|
|
264
|
+
question: Mapped[str] = mapped_column(Text)
|
|
265
|
+
context_json: Mapped[str] = mapped_column(Text, default="{}")
|
|
266
|
+
events_json: Mapped[str] = mapped_column(Text, default="[]")
|
|
267
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
|
268
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
269
|
+
DateTime(timezone=True),
|
|
270
|
+
default=utc_now,
|
|
271
|
+
onupdate=utc_now,
|
|
272
|
+
)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from gaard_core.errors import ConfigurationError
|
|
2
|
-
from gaard_core.investigation.models import InvestigationContext
|
|
3
2
|
from gaard_core.json_utils import json_dumps
|
|
4
3
|
from gaard_core.prompt_compiler.models import CompiledPrompt, SqlGenerationPromptRequest
|
|
5
4
|
from gaard_core.prompt_compiler.schema_formatter import SchemaPromptFormatter
|
|
@@ -54,9 +53,7 @@ class MetadataSqlGenerationPromptCompiler:
|
|
|
54
53
|
return request.formatted_schema
|
|
55
54
|
|
|
56
55
|
if request.database_schema is None:
|
|
57
|
-
raise ConfigurationError(
|
|
58
|
-
"Either database_schema or formatted_schema must be provided."
|
|
59
|
-
)
|
|
56
|
+
raise ConfigurationError("Either database_schema or formatted_schema must be provided.")
|
|
60
57
|
|
|
61
58
|
return self.schema_formatter.format(request.database_schema)
|
|
62
59
|
|
|
@@ -88,37 +85,6 @@ class MetadataIntentClassificationPromptCompiler:
|
|
|
88
85
|
)
|
|
89
86
|
|
|
90
87
|
|
|
91
|
-
class MetadataInvestigationReadinessPromptCompiler:
|
|
92
|
-
def __init__(self, prompt_template: PromptTemplate) -> None:
|
|
93
|
-
self.prompt_template = prompt_template
|
|
94
|
-
|
|
95
|
-
def compile(self, context: InvestigationContext) -> CompiledPrompt:
|
|
96
|
-
payload = {
|
|
97
|
-
"question": context.question,
|
|
98
|
-
"datasource_id": context.datasource_id,
|
|
99
|
-
"user_id": context.user_id,
|
|
100
|
-
"schema": context.formatted_schema,
|
|
101
|
-
"business_logic": context.business_logic,
|
|
102
|
-
}
|
|
103
|
-
payload_json = json_dumps(payload, ensure_ascii=False, indent=2)
|
|
104
|
-
|
|
105
|
-
return CompiledPrompt(
|
|
106
|
-
system_prompt=self.prompt_template.system_prompt,
|
|
107
|
-
user_prompt=self.prompt_template.user_prompt_template.format(
|
|
108
|
-
payload=payload_json,
|
|
109
|
-
question=context.question,
|
|
110
|
-
datasource_id=context.datasource_id,
|
|
111
|
-
user_id=context.user_id,
|
|
112
|
-
schema=context.formatted_schema,
|
|
113
|
-
business_logic=context.business_logic,
|
|
114
|
-
),
|
|
115
|
-
metadata={
|
|
116
|
-
"prompt_key": self.prompt_template.prompt_key,
|
|
117
|
-
"prompt_version": self.prompt_template.version,
|
|
118
|
-
},
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
88
|
class MetadataResultInterpretationPromptCompiler:
|
|
123
89
|
def __init__(self, prompt_template: PromptTemplate) -> None:
|
|
124
90
|
self.prompt_template = prompt_template
|
|
@@ -204,17 +170,6 @@ def get_intent_classification_prompt_compiler() -> (
|
|
|
204
170
|
return MetadataIntentClassificationPromptCompiler(prompt_template=prompt_template)
|
|
205
171
|
|
|
206
172
|
|
|
207
|
-
def get_investigation_readiness_prompt_compiler() -> (
|
|
208
|
-
MetadataInvestigationReadinessPromptCompiler | None
|
|
209
|
-
):
|
|
210
|
-
prompt_template = get_active_prompt_template_safe("investigation_readiness")
|
|
211
|
-
|
|
212
|
-
if prompt_template is None:
|
|
213
|
-
return None
|
|
214
|
-
|
|
215
|
-
return MetadataInvestigationReadinessPromptCompiler(prompt_template=prompt_template)
|
|
216
|
-
|
|
217
|
-
|
|
218
173
|
def get_result_interpretation_prompt_compiler() -> (
|
|
219
174
|
MetadataResultInterpretationPromptCompiler | None
|
|
220
175
|
):
|