You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A full-stack AI-powered chat application combining **SQLPage**, **LiteLLM**, and a **React-based Assistant UI**, designed to query a Resource Surveillance State Database (RSSD) using natural language.
4
+
5
+
## Overview
6
+
7
+
QualityFolio Chat allows users to ask natural language questions about quality data. It uses LiteLLM as an LLM proxy (supporting OpenAI-compatible models including local Ollama models), SQLPage to serve a web UI from SQL, and a React chat widget for the frontend.
8
+
9
+
---
10
+
11
+
## Requirements
12
+
13
+
### System
14
+
15
+
| Requirement | Version / Notes |
16
+
|---|---|
17
+
|**Node.js**| v18+ |
18
+
|**Python**| 3.10+ |
19
+
|**npm**| v9+ |
20
+
|**SQLPage**| Latest (binary in PATH) |
21
+
|**Spry CLI**| Installed and in PATH |
22
+
|**Ollama***(optional)*| Required if using local models (e.g. `oss-20b-32K:latest`) |
23
+
24
+
### Python Packages
25
+
26
+
Install via pip:
27
+
28
+
```bash
29
+
pip install 'litellm[proxy]'
30
+
```
31
+
32
+
Or if using a virtual environment (recommended):
33
+
34
+
```bash
35
+
python -m venv litellm-venv
36
+
source litellm-venv/bin/activate
37
+
pip install 'litellm[proxy]'
38
+
```
39
+
40
+
### Node Packages (Frontend)
41
+
42
+
Installed automatically via `npm install` inside `assistant-ui-chat/`.
> **Note:** Ensure `.env` contains all required API keys or model endpoint URLs. See `.env.example` for reference.
98
+
99
+
### Step 5 — Start the Frontend
100
+
101
+
Install dependencies and run the React dev server:
102
+
103
+
```bash
104
+
cd assistant-ui-chat
105
+
npm install
106
+
npm run dev
107
+
```
108
+
109
+
The frontend will be available at `http://localhost:3000` (or as configured).
110
+
111
+
---
112
+
113
+
## Environment Variables
114
+
115
+
Copy `.env.example` to `.env` and fill in your values:
116
+
117
+
```bash
118
+
cp .env.example .env
119
+
```
120
+
121
+
Key variables to configure:
122
+
123
+
| Variable | Description |
124
+
|---|---|
125
+
|`OPENAI_API_KEY`| OpenAI API key (if using OpenAI models) |
126
+
|`OLLAMA_BASE_URL`| Ollama base URL (default: `http://localhost:11434`) |
127
+
|`DATABASE_URL`| Path or connection string for the RSSD database |
128
+
129
+
---
130
+
131
+
## Troubleshooting
132
+
133
+
### `UnboundLocalError: cannot access local variable 'completion_output'`
134
+
135
+
This is a known bug in some versions of LiteLLM when using tool-calling models with streaming. It is non-blocking but can be resolved by upgrading LiteLLM:
136
+
137
+
```bash
138
+
pip install --upgrade 'litellm[proxy]'
139
+
```
140
+
141
+
### LiteLLM: "upstream model provider is currently experiencing high demand"
142
+
143
+
This is a transient error from the model provider. Wait a moment and retry, or switch to a different model in `litellm_config.yaml`.
144
+
145
+
### SQLPage not serving updated files
146
+
147
+
Re-run Steps 1 and 2 to regenerate `dev-src.auto/`, then restart SQLPage.
148
+
149
+
### Frontend not connecting to chat API
150
+
151
+
Ensure LiteLLM is running (Step 4) and that the API endpoint in `assistant-ui-chat` matches the LiteLLM proxy address (typically `http://localhost:4000`).
? `\n\nAvailable tables and views in the RSSD (use exact names, do not guess pluralizations or variations):\n${knownTables.join(", ")}.`
89
+
: "";
90
+
91
+
constsystemPrompt=`You are an AI assistant connected to a surveilr Resource Surveillance State Database (RSSD) via an MCP server. Your primary capability is answering questions by generating and executing SQL queries against the RSSD — a read-only SQLite database.
92
+
93
+
Use a "Progressive Discovery" strategy: start with lightweight tools and escalate only when needed. You have a maximum of 15 tool calls per response — use them efficiently.
94
+
95
+
Core Constraints:
96
+
- Read-only: Only SELECT statements are permitted. Never attempt INSERT, UPDATE, DELETE, DROP, or any DDL.
97
+
- Row limits: Queries return 10 rows by default, max 50 rows. Request more explicitly only when truly necessary.
98
+
- Text truncation: All text fields are truncated at 200 characters. If a value ends with "... (N chars total)", the full value is longer than displayed.
99
+
- Step budget: You have at most 15 tool calls per response. Prefer the minimum number of calls needed.
100
+
101
+
Available MCP Tools:
102
+
1. Schema Discovery (use these FIRST):
103
+
- list_tables(): ~50-100 tokens. Use at the start of a new conversation to see what tables exist.
104
+
- get_table_columns(table_name): ~50-200 tokens. Use once you know which tables are relevant.
105
+
- get_table_metadata(table_name): Detailed column definitions for a specific table.
106
+
- get_schema_compact(): ~2k-5k tokens. Use when you need a broad overview of the full database structure.
107
+
- get_schema(): ~25k-80k tokens. Use only when full metadata and row counts are explicitly required.
108
+
109
+
2. Data Sampling:
110
+
- get_table_sample(table_name): Returns first 3 rows from a table; text fields truncated to 200 chars.
111
+
- get_table_stats(table_name): Get row count and basic stats for a table.
112
+
113
+
3. Query Execution:
114
+
- query_sql(sql, limit?): Execute a SELECT query. Default 10 rows, max 50 rows.
115
+
116
+
4. Ontology Tools:
117
+
- query_ontology(concept): Look up a concept in the RSSD ontology.
118
+
- explore_concept(class_name): Explore relationships connected to an ontology class.
119
+
- list_ontology(): List available ontology classes.
120
+
121
+
Optimal Text-to-SQL Workflow:
122
+
1. MAP: Call \`list_tables()\` first (only if you don't already know the schema from this conversation) to identify candidate tables.
123
+
2. DRILL: Call \`get_table_columns(table_name)\` for only 1-2 tables that look relevant to the user's question.
124
+
3. INSPECT: Call \`get_table_sample(table_name)\` to see example values (text is truncated for efficiency).
125
+
4. QUERY: Use \`query_sql\` with narrow SELECT statements and specific WHERE clauses.
126
+
127
+
- If a user asks for "passed tests," look for QualityFolio (QF) or evidence tables in the schema.
128
+
- Always prefer small, targeted tool calls over broad discovery.
129
+
- When a query returns no results, try relaxing WHERE filters or checking column values via \`get_table_sample\` before concluding the data doesn't exist.
130
+
131
+
Analysis & Recommendations:
132
+
- After retrieving data, ALWAYS provide analysis and actionable recommendations when the user asks for insights, improvements, or recommendations.
133
+
- When asked about improving test pass rates: query relevant test result data, identify failing patterns, and suggest concrete improvement steps based on the data found.
134
+
- When asked about trends: compare data across time, test suites, or categories and highlight notable patterns.
135
+
- When asked for recommendations: base them on actual data retrieved from the RSSD and supplement with testing best practices.
136
+
- Never refuse to provide recommendations simply because you are a database tool — you are an AI analyst that uses the database as your data source.
137
+
- If the data is insufficient to give a full recommendation, state what data was found and what additional data would help.
138
+
139
+
Behavioral Rules:
140
+
1. Always start with list_tables() on the FIRST turn of a conversation. On subsequent turns, reuse schema already discovered — do not re-run list_tables() or get_table_columns() for already-inspected tables.
141
+
2. Never call get_schema() unless the user explicitly asks for full schema metadata. It is expensive (25k-80k tokens).
142
+
3. Chain tools efficiently: list_tables -> get_table_columns -> query_sql is the default happy path.
143
+
4. Validate before querying: Confirm table and column names exist via discovery tools before writing SQL. Do not guess column names.
144
+
5. Explain truncation: If a text result ends with "... (N chars total)", inform the user the value was truncated and offer to query with a targeted filter.
145
+
6. Limit discipline: Default to limit=10. Only increase to max 50 if the user explicitly needs more data.
146
+
7. SQL safety: Never generate or execute non-SELECT SQL. If the user asks to modify data, explain that the MCP server is read-only.
147
+
8. Surface ontology when relevant: If the user's question involves concepts, classifications, or taxonomy, consider list_ontology() or query_ontology() before writing SQL.
148
+
9. Empty results: If a query returns no rows, inform the user, suggest possible reasons (wrong filter value, different column name), and offer a follow-up query to verify.
149
+
10. Silent execution: Never narrate tool calls, discovery steps, or intermediate findings in the response. Only output the final answer.
150
+
151
+
Anti-Patterns to Avoid:
152
+
- Calling get_schema() on the first turn "just to be safe".
153
+
- Guessing column names without calling get_table_columns() first.
154
+
- Requesting limit=50 when the user only asked for a summary.
155
+
- Re-running list_tables() or get_table_columns() for tables already inspected in this conversation.
156
+
- Treating truncated text values as the full value.
157
+
- Writing JOINs without first confirming the join key columns exist in both tables.${tableHint}`;
158
+
159
+
constresult=streamText({
160
+
model: open_model(process.env.AI_MODEL!),
161
+
tools: tools,
162
+
messages: awaitconvertToModelMessages(messages),
163
+
system: systemPrompt,
164
+
stopWhen: stepCountIs(15),
165
+
onStepFinish: async({ toolResults })=>{
166
+
if(toolResults.length){
167
+
console.log(JSON.stringify(toolResults,null,2));
168
+
}
169
+
},
170
+
});
171
+
172
+
returnresult.toUIMessageStreamResponse({
173
+
onError: (err)=>{
174
+
console.error("STREAM ERROR:",err);
175
+
returnerrinstanceofError ? err.message : "An error occurred while processing your request.";
176
+
},
177
+
});
178
+
}catch(err){
179
+
console.error("API ERROR:",err);
180
+
consterrorMessage=
181
+
errinstanceofError ? err.message : "An unexpected server error occurred.";
0 commit comments