@graphoria/server 0.2.2 → 0.2.3
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.
- package/dist/src/config/types/ai.d.ts +0 -6
- package/dist/src/config/types/ai.d.ts.map +1 -1
- package/dist/src/config/types/auth.d.ts +0 -29
- package/dist/src/config/types/auth.d.ts.map +1 -1
- package/dist/src/config/types/cron.d.ts +0 -94
- package/dist/src/config/types/cron.d.ts.map +1 -1
- package/dist/src/config/types/db.d.ts +0 -87
- package/dist/src/config/types/db.d.ts.map +1 -1
- package/dist/src/config/types/queue.d.ts +0 -47
- package/dist/src/config/types/queue.d.ts.map +1 -1
- package/dist/src/config/types/remote-rest.d.ts +0 -9
- package/dist/src/config/types/remote-rest.d.ts.map +1 -1
- package/dist/src/config/types/remote-schema.d.ts +0 -10
- package/dist/src/config/types/remote-schema.d.ts.map +1 -1
- package/dist/src/cron/index.d.ts +1 -1
- package/dist/src/cron/index.d.ts.map +1 -1
- package/dist/src/cron/index.test.d.ts +2 -0
- package/dist/src/cron/index.test.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -13
- package/playgrounds/console/index.html +7 -7
- package/playgrounds/graphiql/index.html +535 -536
- package/playgrounds/scalar/index.html +1 -1
- package/src/cron/index.ts +6 -2
- package/src/ai/README.md +0 -257
package/src/cron/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { logger } from "../logging";
|
|
|
13
13
|
*/
|
|
14
14
|
export interface ScheduledCronJob {
|
|
15
15
|
name: string;
|
|
16
|
-
job: Cron
|
|
16
|
+
job: Cron<NonNullable<CronJob["context"]>>;
|
|
17
17
|
config: CronJob;
|
|
18
18
|
executionCount: number;
|
|
19
19
|
pause: () => boolean;
|
|
@@ -106,7 +106,11 @@ const createScheduledJob = (config: CronJob, gqlQuery: GqlQueryFn<true>): Schedu
|
|
|
106
106
|
name: config.name,
|
|
107
107
|
job,
|
|
108
108
|
config,
|
|
109
|
-
|
|
109
|
+
// Getter, not a copy: the counter lives in the tick closure and callers
|
|
110
|
+
// (console status, getSummary) read it after ticks have happened.
|
|
111
|
+
get executionCount() {
|
|
112
|
+
return executionCount;
|
|
113
|
+
},
|
|
110
114
|
pause: () => {
|
|
111
115
|
job.pause();
|
|
112
116
|
log.info("paused");
|
package/src/ai/README.md
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
# ollama_helper
|
|
2
|
-
|
|
3
|
-
An LLM-powered database query agent that uses tool-calling to discover a PostgreSQL schema, inspect tables, and execute queries — all from a natural language prompt. It is **cross-LLM**: the same agent runs against Ollama (default, local), OpenAI, DeepSeek, Anthropic, and any OpenAI-compatible endpoint — selected with a single env var.
|
|
4
|
-
|
|
5
|
-
## How It Works
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
User prompt
|
|
9
|
-
│
|
|
10
|
-
▼
|
|
11
|
-
┌─────────────────────────────────────────────────┐
|
|
12
|
-
│ createAgent({ tools, systemPrompt, wrap }) │
|
|
13
|
-
│ → (prompt) => Promise<string> │
|
|
14
|
-
│ │
|
|
15
|
-
│ Agent loop (ask function) │
|
|
16
|
-
│ 1. Send systemPrompt + wrap(prompt) to LLM │
|
|
17
|
-
│ 2. LLM responds with tool_calls? │
|
|
18
|
-
│ ├─ Yes → strip nulls, validate via Zod, │
|
|
19
|
-
│ │ execute tool, feed result back │
|
|
20
|
-
│ └─ No → return final answer │
|
|
21
|
-
│ 3. Repeat until answer or max iters │
|
|
22
|
-
└─────────────────────────────────────────────────┘
|
|
23
|
-
│
|
|
24
|
-
▼
|
|
25
|
-
Final answer (text only — tool calls hidden)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
The agent follows a fixed 4-step discovery workflow for every query:
|
|
29
|
-
|
|
30
|
-
| Step | Tool | Purpose |
|
|
31
|
-
| ---- | ----------------- | --------------------------------------------------- |
|
|
32
|
-
| 1 | `list_entities` | Find relevant database tables by keyword search |
|
|
33
|
-
| 2 | `describe_entity` | Get column names, types, and relationships |
|
|
34
|
-
| 3 | `query_data` | Execute the query using structured JSON (preferred) |
|
|
35
|
-
| 3\* | `graphql_execute` | Raw GraphQL fallback for complex queries |
|
|
36
|
-
| 4 | — | Present results as a Markdown table |
|
|
37
|
-
|
|
38
|
-
Anti-hallucination guards prevent the model from answering without actually querying data.
|
|
39
|
-
|
|
40
|
-
## Quick Start
|
|
41
|
-
|
|
42
|
-
**Prerequisites:** an LLM backend. The default is [Ollama](https://ollama.com) (local, free, no API key) running a tool-capable model. To use a hosted provider instead, set `LLM_PROVIDER` and the matching API key (see [Providers](#providers)).
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
# Install dependencies
|
|
46
|
-
bun install
|
|
47
|
-
|
|
48
|
-
# Run with the default provider (Ollama) and default prompt
|
|
49
|
-
bun run index.ts
|
|
50
|
-
|
|
51
|
-
# Custom prompt
|
|
52
|
-
bun run index.ts "how many students have an active subscription?"
|
|
53
|
-
|
|
54
|
-
# Switch providers via env vars
|
|
55
|
-
LLM_PROVIDER=openai LLM_MODEL=gpt-4o-mini OPENAI_API_KEY=sk-... bun run index.ts "list all events"
|
|
56
|
-
LLM_PROVIDER=deepseek LLM_MODEL=deepseek-chat DEEPSEEK_API_KEY=sk-... bun run index.ts "list all events"
|
|
57
|
-
LLM_PROVIDER=anthropic LLM_MODEL=claude-haiku-4-5-20251001 ANTHROPIC_API_KEY=sk-ant-... bun run index.ts "list all events"
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Using as a library
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { createAgent } from "ollama_helper";
|
|
64
|
-
import { tools } from "./src/tools";
|
|
65
|
-
|
|
66
|
-
// Bind domain config once — call with just a prompt
|
|
67
|
-
const ask = createAgent({
|
|
68
|
-
tools,
|
|
69
|
-
systemPrompt: "You are a database assistant…",
|
|
70
|
-
wrap: (prompt) => `Database query:\n> ${prompt}`,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const answer = await ask("list contacts grouped by role");
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Environment Variables
|
|
77
|
-
|
|
78
|
-
| Variable | Default | Description |
|
|
79
|
-
| ------------------- | ------------------------ | ----------------------------------------------------------- |
|
|
80
|
-
| `LLM_PROVIDER` | `ollama` | Backend: `ollama`, `openai`, `deepseek`, or `anthropic` |
|
|
81
|
-
| `LLM_MODEL` | per-provider | Overrides the provider's default model |
|
|
82
|
-
| `OPENAI_API_KEY` | — | Required when `LLM_PROVIDER=openai` |
|
|
83
|
-
| `OPENAI_BASE_URL` | — | Optional: any OpenAI-compatible endpoint (Groq, Mistral, …) |
|
|
84
|
-
| `DEEPSEEK_API_KEY` | — | Required when `LLM_PROVIDER=deepseek` |
|
|
85
|
-
| `ANTHROPIC_API_KEY` | — | Required when `LLM_PROVIDER=anthropic` |
|
|
86
|
-
| `OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
|
|
87
|
-
| `OLLAMA_MODEL` | `gemma4:e2b` | Ollama model (alias of `LLM_MODEL`, kept for back-compat) |
|
|
88
|
-
|
|
89
|
-
Defined in `.env` or inline on the command line.
|
|
90
|
-
|
|
91
|
-
## Providers
|
|
92
|
-
|
|
93
|
-
The agent talks to every backend through a small `Provider` interface (`src/providers/`). Two wire formats cover everything:
|
|
94
|
-
|
|
95
|
-
| Provider | `LLM_PROVIDER` | Default model | How it connects |
|
|
96
|
-
| ---------------- | -------------- | --------------------------- | ---------------------------------- |
|
|
97
|
-
| Ollama (default) | `ollama` | `gemma4:e2b` | Native `/api/chat`, local, no key |
|
|
98
|
-
| OpenAI | `openai` | `gpt-4o-mini` | `openai` SDK |
|
|
99
|
-
| DeepSeek | `deepseek` | `deepseek-chat` | `openai` SDK (OpenAI-compatible) |
|
|
100
|
-
| Anthropic | `anthropic` | `claude-haiku-4-5-20251001` | `@anthropic-ai/sdk` (Messages API) |
|
|
101
|
-
|
|
102
|
-
Because the OpenAI adapter is just a `baseURL` + key, **any OpenAI-compatible endpoint** (Groq, Mistral, Together, a local `/v1` server) works by pointing `OPENAI_BASE_URL` at it — no new code. Adding a first-class provider is one entry in the registry in `src/providers/index.ts`.
|
|
103
|
-
|
|
104
|
-
The agent loop and anti-hallucination guards are provider-agnostic; each adapter only translates the normalized message/tool-call history to and from its provider's wire format.
|
|
105
|
-
|
|
106
|
-
## Recommended Ollama Models
|
|
107
|
-
|
|
108
|
-
| Model | Size | Quality | Notes |
|
|
109
|
-
| -------------- | ------ | ------- | --------------------------------------------------------------------------------------------- |
|
|
110
|
-
| **gemma4:e2b** | 7.2 GB | ★★★★★ | **Default.** Native function-calling, built for agentic workflows. Google-recommended params. |
|
|
111
|
-
| gemma4:e4b | 9.6 GB | ★★★★★ | Stronger reasoning, heavier. Same family, better accuracy. |
|
|
112
|
-
| qwen3.5:2b | 2.7 GB | ★★★★☆ | Good budget option. Fast, decent tool use. |
|
|
113
|
-
| llama3.2 | 2.0 GB | ★★☆☆☆ | Works but hallucinates more. Use `OLLAMA_MODEL=llama3.2` to test. |
|
|
114
|
-
|
|
115
|
-
Each model family gets optimal sampling parameters automatically (temperature, top_p, top_k) based on vendor recommendations.
|
|
116
|
-
|
|
117
|
-
## Project Structure
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
ollama_helper/
|
|
121
|
-
├── index.ts # Library barrel — exports ask, createAgent, AgentConfig
|
|
122
|
-
├── .env # Environment variables (OLLAMA_MODEL, OLLAMA_HOST)
|
|
123
|
-
├── package.json
|
|
124
|
-
├── tsconfig.json
|
|
125
|
-
└── src/
|
|
126
|
-
├── types.ts # Normalized Message/ToolCall + Provider + Tool interfaces
|
|
127
|
-
├── providers/
|
|
128
|
-
│ ├── index.ts # getProvider() — selects the backend from LLM_PROVIDER
|
|
129
|
-
│ ├── ollama.ts # Native /api/chat adapter + model presets (default)
|
|
130
|
-
│ ├── openai.ts # OpenAI-compatible adapter (OpenAI, DeepSeek, …)
|
|
131
|
-
│ └── anthropic.ts # Anthropic Messages API adapter
|
|
132
|
-
├── tools.ts # 4 tool definitions (Zod schemas + mock executors)
|
|
133
|
-
└── agent.ts # Agent loop: ask() + createAgent() factory
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## API
|
|
137
|
-
|
|
138
|
-
### `createAgent(config: AgentConfig): (prompt: string) => Promise<string>`
|
|
139
|
-
|
|
140
|
-
Pre-configure an agent with tools, system prompt, and a prompt wrapper. Returns a single-argument function — call it with any prompt string.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { createAgent } from "ollama_helper";
|
|
144
|
-
|
|
145
|
-
const ask = createAgent({
|
|
146
|
-
tools: [...], // Tool[] — Zod-based tool definitions
|
|
147
|
-
systemPrompt: "You are a helpful agent.", // System-level instruction to the LLM
|
|
148
|
-
wrap: (p) => `User query:\n> ${p}`, // Wraps the raw prompt into the user message
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const answer = await ask("list all contacts grouped by role");
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### `ask(prompt, tools, systemPrompt, wrap): Promise<string>`
|
|
155
|
-
|
|
156
|
-
Full-control entry point. Same as `createAgent` but without pre-binding — pass all 4 arguments per call.
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
import { ask } from "ollama_helper";
|
|
160
|
-
|
|
161
|
-
const answer = await ask(
|
|
162
|
-
"list contacts grouped by role",
|
|
163
|
-
tools,
|
|
164
|
-
"You are a database assistant.",
|
|
165
|
-
(p) => `Query: ${p}`,
|
|
166
|
-
);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### `AgentConfig`
|
|
170
|
-
|
|
171
|
-
| Field | Type | Description |
|
|
172
|
-
| -------------- | ----------------------------- | ------------------------------------------------------ |
|
|
173
|
-
| `tools` | `Tool[]` | Zod-based tool definitions (schema + executor) |
|
|
174
|
-
| `systemPrompt` | `string` | System-level instruction sent to the LLM |
|
|
175
|
-
| `wrap` | `(content: string) => string` | Transforms the user prompt into the final user message |
|
|
176
|
-
|
|
177
|
-
### `Tool`
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
interface Tool<TSchema extends z.ZodTypeAny> {
|
|
181
|
-
name: string; // Tool name (must match what the LLM calls)
|
|
182
|
-
description: string; // Description sent to the LLM
|
|
183
|
-
schema: TSchema; // Zod schema — auto-converted to JSON Schema
|
|
184
|
-
execute: (args: z.infer<TSchema>) => Promise<unknown>; // Typed executor
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
JSON Schema is auto-generated via `z.toJSONSchema(tool.schema)`. The framework automatically strips `null` values from LLM arguments (common with small models) before Zod validation. Use the exported `jsonCoerce` helper for fields that may arrive as stringified JSON arrays/objects.
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
import { z } from "zod";
|
|
192
|
-
import { jsonCoerce } from "./src/tools";
|
|
193
|
-
|
|
194
|
-
const mySchema = z.object({
|
|
195
|
-
columns: jsonCoerce(z.array(z.string())).optional(), // Handles "[\"id\"]" → ["id"]
|
|
196
|
-
filters: jsonCoerce(z.record(z.string(), z.unknown())).optional(),
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Tools
|
|
201
|
-
|
|
202
|
-
The agent exposes 4 tools to the LLM. Each is defined as a `Tool` object with a Zod schema and executor:
|
|
203
|
-
|
|
204
|
-
| Tool | Description |
|
|
205
|
-
| ----------------- | -------------------------------------------------------------------------------- |
|
|
206
|
-
| `list_entities` | Search for database tables by keyword or category |
|
|
207
|
-
| `describe_entity` | Get column metadata, relationships, and data types for a table |
|
|
208
|
-
| `query_data` | Execute a query using structured JSON (preferred for all list/aggregate queries) |
|
|
209
|
-
| `graphql_execute` | Execute a raw GraphQL query (fallback for complex/nested queries) |
|
|
210
|
-
|
|
211
|
-
**`query_data` input format:**
|
|
212
|
-
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"entity": "contacts",
|
|
216
|
-
"operation": "aggregate",
|
|
217
|
-
"groupBy": ["role"],
|
|
218
|
-
"columns": ["id", "first_name", "last_name"],
|
|
219
|
-
"filters": { "deleted_at": { "is_null": true } },
|
|
220
|
-
"limit": 50
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Filter operators: `eq`, `neq`, `like`, `ilike`, `gt`, `gte`, `lt`, `lte`, `is_null`.
|
|
225
|
-
|
|
226
|
-
## Anti-Hallucination Measures
|
|
227
|
-
|
|
228
|
-
The agent employs multiple layers to prevent the LLM from fabricating data:
|
|
229
|
-
|
|
230
|
-
1. **Zod validation** — All tool arguments validated before execution; invalid args → error fed back to LLM for self-correction
|
|
231
|
-
2. **Null stripping** — `null` values for optional fields (common LLM mistake) automatically stripped pre-parse
|
|
232
|
-
3. **JSON coercion** — Stringified JSON arrays/objects auto-parsed via `jsonCoerce` helper
|
|
233
|
-
4. **Temperature control** — Model-specific presets (e.g., temp 0 for llama3.2 to suppress creativity)
|
|
234
|
-
5. **Guard clause** — If the model tries to answer without calling `query_data` or `graphql_execute`, a corrective nudge is injected and the loop retries
|
|
235
|
-
6. **Negative constraints** — Prompts explicitly forbid fabrication: _"NEVER fabricate, invent, or guess query results"_
|
|
236
|
-
|
|
237
|
-
## Mock Tools
|
|
238
|
-
|
|
239
|
-
When a real Graphoria server is not available, the tools return hardcoded mock data mirroring the capoeira group management schema:
|
|
240
|
-
|
|
241
|
-
- **5 tables:** contacts, student_profiles, classes, payments, events
|
|
242
|
-
- **18 contacts** with roles: 12 students, 1 master, 2 guardians, 3 guests
|
|
243
|
-
- **Full column metadata** for the contacts table (11 columns including soft-delete `deleted_at`)
|
|
244
|
-
|
|
245
|
-
To replace mocks with real server calls, update the executors in `src/tools.ts` to use `fetch()` against the Graphoria REST/GraphQL endpoints.
|
|
246
|
-
|
|
247
|
-
## Design Decisions
|
|
248
|
-
|
|
249
|
-
- **`createAgent` factory** — Pre-bind domain config (tools, system prompt, wrap); clean `ask(prompt)` API for callers
|
|
250
|
-
- **`ask()` for full control** — 4 positional params when callers need per-call variations
|
|
251
|
-
- **Zod schemas for tools** — `z.toJSONSchema()` auto-generates JSON Schema parameters; `z.infer` gives type-safe executor args; no hand-written JSON Schema
|
|
252
|
-
- **`stripNulls` applied automatically** — LLMs pass `null` for optional fields; stripped pre-parse so callers write plain Zod schemas
|
|
253
|
-
- **`jsonCoerce` opt-in helper** — Small LLMs stringify arrays/objects; opt-in per-field coercion
|
|
254
|
-
- **Non-streaming only** (`stream: false`) — simpler loop logic; no need to buffer partial tool_calls across chunks
|
|
255
|
-
- **JSON query tool over raw GraphQL** — structured JSON has far fewer syntax failure modes for LLMs than raw GraphQL string construction
|
|
256
|
-
- **Max 10 iterations** — safety valve to prevent infinite tool-calling loops
|
|
257
|
-
- **Bun runtime** — native fetch, zero-config TypeScript, fast startup
|