stateful-mcp 0.1.1
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/LICENSE +21 -0
- package/README.md +111 -0
- package/config/about/dictionary.md +8 -0
- package/config/about/event.md +8 -0
- package/config/about/filter.md +9 -0
- package/config/about/form.md +9 -0
- package/config/about/log.md +9 -0
- package/config/about/middleware.md +18 -0
- package/config/about/object.md +8 -0
- package/config/about.config.json +37 -0
- package/config/examples/dictionary.md +9 -0
- package/config/examples/event.md +10 -0
- package/config/examples/filter.md +13 -0
- package/config/examples/form.md +23 -0
- package/config/examples/log.md +14 -0
- package/config/examples/object.md +12 -0
- package/config/storage.config.json +39 -0
- package/config/tools.config.json +14 -0
- package/dist/adapters/engines/dataframe-bridge.py +89 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37412 -0
- package/dist/src/adapters/engines/dataframe-engine.d.ts +33 -0
- package/dist/src/adapters/engines/dataframe-engine.d.ts.map +1 -0
- package/dist/src/adapters/engines/interfaces.d.ts +11 -0
- package/dist/src/adapters/engines/interfaces.d.ts.map +1 -0
- package/dist/src/adapters/engines/memory-query.d.ts +12 -0
- package/dist/src/adapters/engines/memory-query.d.ts.map +1 -0
- package/dist/src/adapters/engines/pg-query.d.ts +16 -0
- package/dist/src/adapters/engines/pg-query.d.ts.map +1 -0
- package/dist/src/adapters/engines/sqlite-query.d.ts +16 -0
- package/dist/src/adapters/engines/sqlite-query.d.ts.map +1 -0
- package/dist/src/adapters/storage/browser-repo.d.ts +116 -0
- package/dist/src/adapters/storage/browser-repo.d.ts.map +1 -0
- package/dist/src/adapters/storage/compliance.d.ts +13 -0
- package/dist/src/adapters/storage/compliance.d.ts.map +1 -0
- package/dist/src/adapters/storage/interfaces.d.ts +134 -0
- package/dist/src/adapters/storage/interfaces.d.ts.map +1 -0
- package/dist/src/adapters/storage/jsonl-repo.d.ts +178 -0
- package/dist/src/adapters/storage/jsonl-repo.d.ts.map +1 -0
- package/dist/src/adapters/storage/memory-repo.d.ts +129 -0
- package/dist/src/adapters/storage/memory-repo.d.ts.map +1 -0
- package/dist/src/adapters/storage/pg-repo.d.ts +38 -0
- package/dist/src/adapters/storage/pg-repo.d.ts.map +1 -0
- package/dist/src/adapters/storage/sqlite-repo.d.ts +91 -0
- package/dist/src/adapters/storage/sqlite-repo.d.ts.map +1 -0
- package/dist/src/adapters/validation/references.d.ts +6 -0
- package/dist/src/adapters/validation/references.d.ts.map +1 -0
- package/dist/src/adapters/validation/runner.d.ts +28 -0
- package/dist/src/adapters/validation/runner.d.ts.map +1 -0
- package/dist/src/config/loader.d.ts +33 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +232 -0
- package/dist/src/config/pagination.d.ts +44 -0
- package/dist/src/config/pagination.d.ts.map +1 -0
- package/dist/src/config/types.d.ts +152 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +1 -0
- package/dist/src/config/validator.d.ts +3 -0
- package/dist/src/config/validator.d.ts.map +1 -0
- package/dist/src/errors/format.d.ts +8 -0
- package/dist/src/errors/format.d.ts.map +1 -0
- package/dist/src/errors/types.d.ts +32 -0
- package/dist/src/errors/types.d.ts.map +1 -0
- package/dist/src/errors/types.js +44 -0
- package/dist/src/middleware/dictionary/interfaces.d.ts +43 -0
- package/dist/src/middleware/dictionary/interfaces.d.ts.map +1 -0
- package/dist/src/middleware/dictionary/resolver.d.ts +47 -0
- package/dist/src/middleware/dictionary/resolver.d.ts.map +1 -0
- package/dist/src/middleware/dictionary/store.d.ts +73 -0
- package/dist/src/middleware/dictionary/store.d.ts.map +1 -0
- package/dist/src/middleware/dictionary/types.d.ts +152 -0
- package/dist/src/middleware/dictionary/types.d.ts.map +1 -0
- package/dist/src/middleware/event/store.d.ts +59 -0
- package/dist/src/middleware/event/store.d.ts.map +1 -0
- package/dist/src/middleware/event/types.d.ts +48 -0
- package/dist/src/middleware/event/types.d.ts.map +1 -0
- package/dist/src/middleware/filter/store.d.ts +44 -0
- package/dist/src/middleware/filter/store.d.ts.map +1 -0
- package/dist/src/middleware/filter/types.d.ts +65 -0
- package/dist/src/middleware/filter/types.d.ts.map +1 -0
- package/dist/src/middleware/form/store.d.ts +43 -0
- package/dist/src/middleware/form/store.d.ts.map +1 -0
- package/dist/src/middleware/form/types.d.ts +21 -0
- package/dist/src/middleware/form/types.d.ts.map +1 -0
- package/dist/src/middleware/object/schema-walker.d.ts +4 -0
- package/dist/src/middleware/object/schema-walker.d.ts.map +1 -0
- package/dist/src/middleware/object/store.d.ts +62 -0
- package/dist/src/middleware/object/store.d.ts.map +1 -0
- package/dist/src/middleware/object/types.d.ts +19 -0
- package/dist/src/middleware/object/types.d.ts.map +1 -0
- package/dist/src/services/dictionary.d.ts +2 -0
- package/dist/src/services/dictionary.d.ts.map +1 -0
- package/dist/src/services/event.d.ts +2 -0
- package/dist/src/services/event.d.ts.map +1 -0
- package/dist/src/services/filter.d.ts +2 -0
- package/dist/src/services/filter.d.ts.map +1 -0
- package/dist/src/services/form.d.ts +2 -0
- package/dist/src/services/form.d.ts.map +1 -0
- package/dist/src/services/helper.d.ts +8 -0
- package/dist/src/services/helper.d.ts.map +1 -0
- package/dist/src/services/log.d.ts +2 -0
- package/dist/src/services/log.d.ts.map +1 -0
- package/dist/src/services/object.d.ts +2 -0
- package/dist/src/services/object.d.ts.map +1 -0
- package/dist/src/translation/compiler.d.ts +3 -0
- package/dist/src/translation/compiler.d.ts.map +1 -0
- package/dist/src/translation/pipeline.d.ts +3 -0
- package/dist/src/translation/pipeline.d.ts.map +1 -0
- package/dist/src/translation/resolver.d.ts +4 -0
- package/dist/src/translation/resolver.d.ts.map +1 -0
- package/dist/src/translation/types.d.ts +32 -0
- package/dist/src/translation/types.d.ts.map +1 -0
- package/dist/src/translation/validator.d.ts +3 -0
- package/dist/src/translation/validator.d.ts.map +1 -0
- package/docs/browser.md +96 -0
- package/docs/config.md +498 -0
- package/docs/dictionary.md +37 -0
- package/docs/event.md +35 -0
- package/docs/filter.md +32 -0
- package/docs/log.md +54 -0
- package/docs/object.md +29 -0
- package/docs/pipeline.md +116 -0
- package/docs/resource_types.md +138 -0
- package/docs/validation_examples.md +230 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 stateful-mcp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Stateful MCP Middleware Suite
|
|
2
|
+
|
|
3
|
+
A modular, version-controlled [Model Context Protocol (MCP)](https://modelcontextprotocol.io) middleware suite that helps LLMs manage stateful workflows incrementally across conversational turns — filters, objects, event logs, and a terminology dictionary — optimizing token consumption and avoiding huge payloads.
|
|
4
|
+
|
|
5
|
+
Each service is exposed as an MCP server over stdio and launched via the monolith router.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g stateful-mcp # or: npm install stateful-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **Runtime:** Bun (>= 1.0) is required to run the server, because the SQLite storage adapter uses `bun:sqlite`. The Memory and PostgreSQL adapters work under any supported runtime; only the SQLite adapter needs Bun.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Launch a service (reads config from ./config by default)
|
|
23
|
+
SERVICE_TYPE=filter stateful-mcp
|
|
24
|
+
|
|
25
|
+
# Or point at an explicit config directory
|
|
26
|
+
stateful-mcp --config-dir /path/to/my-config
|
|
27
|
+
SERVICE_TYPE=object stateful-mcp -c /path/to/my-config
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The binary maps to the monolith router in `index.ts`. `SERVICE_TYPE` selects the service: `filter`, `object`, `dictionary`, `log`, or `event` (defaults to `filter`).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
Configuration lives in the **config directory** and is split by concern:
|
|
37
|
+
|
|
38
|
+
| File | Purpose |
|
|
39
|
+
|------|---------|
|
|
40
|
+
| `config/tools.config.json` | Tool schemas, validation engines, and query backends. |
|
|
41
|
+
| `config/storage.config.json` | Storage backends (memory / SQLite / PostgreSQL) for session & persistent state, auto-compression thresholds, and `pagination_limits`. |
|
|
42
|
+
| `config/about.config.json` | Markdown docs served by the `*_about` / `*_examples` developer-guidance tools (optional). |
|
|
43
|
+
|
|
44
|
+
### Where config is read from
|
|
45
|
+
|
|
46
|
+
`loadMiddlewareConfig` resolves the config directory in this order:
|
|
47
|
+
|
|
48
|
+
1. `--config-dir <path>` / `-c <path>` / `--config-dir=<path>` (CLI flag)
|
|
49
|
+
2. `STATEFUL_MCP_CONFIG_DIR` (environment variable)
|
|
50
|
+
3. `process.cwd()` (fallback)
|
|
51
|
+
|
|
52
|
+
Ship your own `config/` and point the package at it — you do **not** rely on the publisher's demo config. A single-file `filter.config.json` (or `config.json`) at the config root is also supported.
|
|
53
|
+
|
|
54
|
+
### Environment variables
|
|
55
|
+
|
|
56
|
+
| Variable | Required | Purpose |
|
|
57
|
+
|----------|----------|---------|
|
|
58
|
+
| `SERVICE_TYPE` | no | Which service to start (`filter` default). |
|
|
59
|
+
| `STATEFUL_MCP_CONFIG_DIR` | no | Directory containing `config/`. |
|
|
60
|
+
| `LOG_SERVICE_SECRET` | no | 32-byte HMAC key for stateless `log_next` page tokens. Random if unset. |
|
|
61
|
+
| `WORKSPACE_ID` | no | Fallback dictionary workspace id (default `global`). |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Optional: Native DataFrame query engine
|
|
66
|
+
|
|
67
|
+
The `dataframe` query engine runs DuckDB SQL natively over CSV, Parquet, or JSONL files.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Building from source
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bun install
|
|
75
|
+
npm run build # bundles dist/index.js (bun build) + emits .d.ts + copies assets
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`prepublishOnly` runs `npm run build`, so `npm publish` always ships a built package.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Services
|
|
83
|
+
|
|
84
|
+
1. **Filter Service** — build multi-clause database queries incrementally (VCS-style rule DAG).
|
|
85
|
+
2. **Object Service** — manage structured JSON objects with stateful delta tracking and templating.
|
|
86
|
+
3. **Dictionary Service** — normalize abbreviations to standardized concept identifiers.
|
|
87
|
+
4. **Event Service** — version-controlled append-only logs with branching, LCA merging, and conflict resolution.
|
|
88
|
+
5. **Log Service** — stateless, paginated traversal over filter/object history using HMAC-signed page tokens.
|
|
89
|
+
6. **Form Service** — manage stateful interactive forms with branching logic, step validations, and back-navigation staleness tracking.
|
|
90
|
+
|
|
91
|
+
Each service exposes `*_about` / `*_examples` developer-guidance tools. LLMs should consult them when handling complex state.
|
|
92
|
+
|
|
93
|
+
See `docs/` for the full reference (filter, object, form, event, dictionary, log, config, pipeline, browser).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Client-Side & Browser Storage
|
|
98
|
+
|
|
99
|
+
For web/frontend environments, the suite provides native client-side storage repositories:
|
|
100
|
+
* **LocalStorage**: Simple web storage for session/persistent state.
|
|
101
|
+
* **IndexedDB**: Non-blocking asynchronous store for larger state histories.
|
|
102
|
+
|
|
103
|
+
See [docs/browser.md](docs/browser.md) for details on integrating client-side adapters.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Compliance Test Suite
|
|
108
|
+
|
|
109
|
+
Verify that custom third-party storage adapters strictly comply with the stateful consistency and isolation contract:
|
|
110
|
+
* Exposes `runStoreComplianceTests`, `runFormStoreComplianceTests`, `runObjectStoreComplianceTests`, and `runEventStoreComplianceTests` via `"stateful-mcp/adapters/compliance"`.
|
|
111
|
+
* Asserts uniqueness, routing, isolation, TTL, and VCS compaction correctness.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Dictionary Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Dictionary Service normalizes clinical or business terms to their authoritative coordinate mappings.
|
|
4
|
+
|
|
5
|
+
## Core Rules
|
|
6
|
+
* **Normalize Early**: Always resolve vernacular terminology (e.g. abbreviations, aliases) to formal concept codes before using them in filter conditions or object field values.
|
|
7
|
+
* **Explore**: Use `dictionary_find` to discover terms and synonyms.
|
|
8
|
+
* **Scope Resolution**: Personal aliases shadow global configurations. Rely on the resolved priority outputs.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Event Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Event Service models structured append-only event logs (e.g. observation flows, order histories) via version-controlled commit DAG chains.
|
|
4
|
+
|
|
5
|
+
## Multi-turn Conflict Management
|
|
6
|
+
* **Merge Conflicts**: When merging parallel updates on identical event IDs, a merge conflict is raised.
|
|
7
|
+
* **Inspect & Resolve**: Call `event_merge_inspect` to view conflicting values, and resolve them step-by-step using `event_merge_resolve` strategies.
|
|
8
|
+
* **Commit**: Call `event_merge_commit` only when all conflicts are resolved.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Filter Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Filter Service builds database queries incrementally. It is designed to save context window tokens and allow interactive redirection by the user.
|
|
4
|
+
|
|
5
|
+
## Intentional Strategy
|
|
6
|
+
* **Incremental Construction**: Do NOT call `filter_add` with all conditions at once. Emit one or two conditions, inspect, and continue. This allows the user to correct you before you waste compute/tokens.
|
|
7
|
+
* **Inspect before Execution**: Always call `filter_inspect` before executing a query to ensure you are executing the correct filter state.
|
|
8
|
+
* **Compression**: Call `filter_compress` (or let auto-compression trigger) to flatten long linear version chains and keep execution performance high.
|
|
9
|
+
* **Branching**: Use the version chain to branch when a user changes their mind rather than rebuilding the filter from scratch.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Form Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Form Service manages dynamically branched questionnaires and survey forms. It operates as a stateful graph engine where next questions are resolved dynamically on-the-fly based on prior answers and skip actions.
|
|
4
|
+
|
|
5
|
+
## Rules of Engagement
|
|
6
|
+
* **Incremental Answering**: Call `form_answer` or `form_skip` after each response. The returned `next_questions` list will tell you which question(s) are now active and need to be asked.
|
|
7
|
+
* **Back-Navigation**: If the user wants to revise a previous choice, call `form_back` with the target question. The engine preserves subsequent answers where possible but flags them as `stale` if the branching path shifts.
|
|
8
|
+
* **Skipping Optional Fields**: Non-required questions can be explicitly bypassed using `form_skip`.
|
|
9
|
+
* **Resolve Final State**: Once the form indicates `complete: true`, call `form_resolve` to consolidate all active answers, automatically filtering out any stale answers from inactive branching paths.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Log Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Log Service is a read-only time machine over Filter and Object history. It is designed for inspection, audit, and explanation — never for mutation.
|
|
4
|
+
|
|
5
|
+
## Intentional Strategy
|
|
6
|
+
* **Use it to explain lineage**: When a user asks "how did this filter/object get to this state?", open a log traversal and walk back, summarizing each checkpoint's delta.
|
|
7
|
+
* **Page, don't dump**: Always pass a modest `limit` to `log_open` and follow `next_page_token` with `log_next`. Large histories should be consumed incrementally to protect the context window.
|
|
8
|
+
* **Pair with the owning service**: A `delta`/`rules` entry from the Log Service tells you *what changed* at a checkpoint. To see the *full* state at that point, call `filter_get_filter` / `object_get` on the returned `id`.
|
|
9
|
+
* **Remember it is ephemeral**: The Log Service only sees checkpoints still present in the session store. After `filter_gc` / `object_gc`, older history is gone — traverse before pruning.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Middleware Architecture & Coordination
|
|
2
|
+
|
|
3
|
+
This middleware exposes four stateful services designed to help you construct complex query filters, validate objects, standardise vocabulary, and track structured log events incrementally.
|
|
4
|
+
|
|
5
|
+
## How the Services Relate
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
A[Dictionary Service] -->|Normalize Vernacular| B[Filter / Object / Event]
|
|
10
|
+
C[Filter Service] -->|Build Structured Queries| E[Database Execution]
|
|
11
|
+
D[Object Service] -->|Validate Structured Inputs| F[Tool Arguments]
|
|
12
|
+
G[Event Service] -->|Track Mutation Log Stream| H[VCS DAG commits]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
1. **Dictionary Service**: Enforces ontology and resolves user shorthand (e.g. "heart attack" -> "myocardial infarction") before values are injected into filters, objects, or tool functions that require domain-specific vocabulary.
|
|
16
|
+
2. **Filter Service**: Allows you to incrementally construct complex query conditions without repeatedly sending the whole filter definition.
|
|
17
|
+
3. **Object Service**: Validates structured object data against schemas, using templates to minimise network traffic.
|
|
18
|
+
4. **Event Service**: Offers a version-controlled, append-only log array representing changes over time with branching and symmetric merging.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Object Service: Strategy & Guidelines
|
|
2
|
+
|
|
3
|
+
The Object Service manages structured JSON objects (such as orders or SOAP notes) using a stateful delta model.
|
|
4
|
+
|
|
5
|
+
## Rules of Engagement
|
|
6
|
+
* **Template Reuse**: Always call `object_from_saved` first to see if there is an existing object template that you can reuse, saving token overhead.
|
|
7
|
+
* **Property set as you go**: Set fields using `object_patch` incrementally as you learn values from the user, rather than calling it with a massive payload on turn one.
|
|
8
|
+
* **Validation Check**: Always call `object_validate` to inspect errors and get a clean diff before finalizing.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about_and_examples": {
|
|
3
|
+
"middleware_about": [
|
|
4
|
+
{ "_type": "file", "path": "config/about/middleware.md" }
|
|
5
|
+
],
|
|
6
|
+
"filter_about": [
|
|
7
|
+
{ "_type": "file", "path": "config/about/filter.md" }
|
|
8
|
+
],
|
|
9
|
+
"filter_examples": [
|
|
10
|
+
{ "_type": "file", "path": "config/examples/filter.md" }
|
|
11
|
+
],
|
|
12
|
+
"object_about": [
|
|
13
|
+
{ "_type": "file", "path": "config/about/object.md" }
|
|
14
|
+
],
|
|
15
|
+
"object_examples": [
|
|
16
|
+
{ "_type": "file", "path": "config/examples/object.md" }
|
|
17
|
+
],
|
|
18
|
+
"dictionary_about": [
|
|
19
|
+
{ "_type": "file", "path": "config/about/dictionary.md" }
|
|
20
|
+
],
|
|
21
|
+
"dictionary_examples": [
|
|
22
|
+
{ "_type": "file", "path": "config/examples/dictionary.md" }
|
|
23
|
+
],
|
|
24
|
+
"event_about": [
|
|
25
|
+
{ "_type": "file", "path": "config/about/event.md" }
|
|
26
|
+
],
|
|
27
|
+
"event_examples": [
|
|
28
|
+
{ "_type": "file", "path": "config/examples/event.md" }
|
|
29
|
+
],
|
|
30
|
+
"log_about": [
|
|
31
|
+
{ "_type": "file", "path": "config/about/log.md" }
|
|
32
|
+
],
|
|
33
|
+
"log_examples": [
|
|
34
|
+
{ "_type": "file", "path": "config/examples/log.md" }
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Dictionary Service Worked Example
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
User: Search for patient notes mentioning a heart attack.
|
|
5
|
+
LLM: Calling `dictionary_resolve(term="heart attack", namespace="CLINICAL")` -> returns "CLINICAL::MYOCARDIAL_INFARCTION"
|
|
6
|
+
LLM: Calling `filter_init(table="clinical_notes", session_id="s1")` -> returns "filter_1"
|
|
7
|
+
LLM: Calling `filter_add(filter_id="filter_1", condition={field: "diagnosis", operator: "equals", value: "CLINICAL::MYOCARDIAL_INFARCTION"})`
|
|
8
|
+
```
|
|
9
|
+
This demonstrates resolving vernacular to a formal coordinate identifier before query execution.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Event Service Worked Example
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
User: Merge stream A and B.
|
|
5
|
+
LLM: Calling `event_merge(session_id="s1", source_ids_or_aliases=["stream_a", "stream_b"], target_id_or_alias="main")` -> returns status: "conflict", merge_session_id: "merge_123"
|
|
6
|
+
LLM: Calling `event_merge_inspect(merge_session_id="merge_123")` -> returns conflicting event ID: "ev_1"
|
|
7
|
+
LLM: Calling `event_merge_resolve(merge_session_id="merge_123", event_id="ev_1", resolution={strategy: "accept_source", source_id: "stream_b"})` -> returns new merge session ID: "merge_124"
|
|
8
|
+
LLM: Calling `event_merge_commit(merge_session_id="merge_124", session_id="s1")` -> returns committed ID "main"
|
|
9
|
+
```
|
|
10
|
+
This shows the step-by-step resolution of conflicts before merge completion.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Filter Service Worked Example
|
|
2
|
+
|
|
3
|
+
Here is a worked example demonstrating incremental filter construction:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
User: Show me active customers who spent more than $100.
|
|
7
|
+
LLM: Calling `filter_init(table="customers", session_id="s1")` -> returns "filter_1"
|
|
8
|
+
LLM: Calling `filter_add(filter_id="filter_1", condition={field: "status", operator: "equals", value: "active"})` -> returns "filter_2"
|
|
9
|
+
LLM: Calling `filter_add(filter_id="filter_2", condition={field: "spend", operator: "greater_than", value: 100})` -> returns "filter_3"
|
|
10
|
+
LLM: Calling `filter_inspect(filter_id="filter_3")` -> returns compiled filter
|
|
11
|
+
LLM: Calling `filter_execute(filter_id="filter_3")` -> returns data
|
|
12
|
+
```
|
|
13
|
+
Notice that the LLM builds the filter incrementally, ensuring correctness at each step.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Form Service Worked Example
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
User: Let's start the patient intake form.
|
|
5
|
+
LLM: Calling `form_init(schema_name="patient_intake", session_id="s1")` -> returns "form_1" (next questions: ["q_name"])
|
|
6
|
+
LLM: What is your full name?
|
|
7
|
+
User: John Doe
|
|
8
|
+
LLM: Calling `form_answer(form_id="form_1", question_id="q_name", value="John Doe", session_id="s1")` -> returns "form_2" (next questions: ["q_age"])
|
|
9
|
+
LLM: What is your age?
|
|
10
|
+
User: 30
|
|
11
|
+
LLM: Calling `form_answer(form_id="form_2", question_id="q_age", value=30, session_id="s1")` -> returns "form_3" (next questions: ["q_smoke"])
|
|
12
|
+
LLM: Do you currently smoke?
|
|
13
|
+
User: Yes
|
|
14
|
+
LLM: Calling `form_answer(form_id="form_3", question_id="q_smoke", value=true, session_id="s1")` -> returns "form_4" (next questions: ["q_smoke_frequency"])
|
|
15
|
+
LLM: How many cigarettes per day do you smoke?
|
|
16
|
+
User: 5
|
|
17
|
+
LLM: Calling `form_answer(form_id="form_4", question_id="q_smoke_frequency", value=5, session_id="s1")` -> returns "form_5" (next questions: ["q_alcohol"])
|
|
18
|
+
LLM: Describe your alcohol usage (optional).
|
|
19
|
+
User: None, please skip.
|
|
20
|
+
LLM: Calling `form_skip(form_id="form_5", question_id="q_alcohol", session_id="s1")` -> returns "form_6" (complete: true)
|
|
21
|
+
LLM: Calling `form_resolve(form_id="form_6", session_id="s1")` -> returns fully compiled form answers
|
|
22
|
+
```
|
|
23
|
+
This shows linear answering, branching based on `q_smoke === true`, optional question skipping, and resolution.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Log Service Worked Example
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
User: Walk me through how this object evolved.
|
|
5
|
+
LLM: Calling `log_open(type="object", session_id="s1", id_or_alias="obj_tip", limit=3)`
|
|
6
|
+
-> returns entries: [obj_tip delta, obj_prev delta, obj_base delta], next_page_token="tok_1", has_more=true
|
|
7
|
+
LLM: (summarize the three deltas to the user)
|
|
8
|
+
User: And before that?
|
|
9
|
+
LLM: Calling `log_next(page_token="tok_1")`
|
|
10
|
+
-> returns entries: [obj_older delta, obj_seed delta], next_page_token=null, has_more=false
|
|
11
|
+
LLM: (root reached — traversal complete)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This demonstrates stateless, paginated traversal of an object's version chain from tip back to its seed checkpoint.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Object Service Worked Example
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
User: Create a new prescription order for Patient 123.
|
|
5
|
+
LLM: Calling `object_init(schema_name="prescription", session_id="s1")` -> returns "obj_1"
|
|
6
|
+
LLM: Calling `object_patch(object_id="obj_1", patch={patient_id: "123"})` -> returns "obj_2"
|
|
7
|
+
User: Set the medication to Amoxicillin and dose to 500mg.
|
|
8
|
+
LLM: Calling `object_patch(object_id="obj_2", patch={medication: "Amoxicillin", dose: "500mg"})` -> returns "obj_3"
|
|
9
|
+
LLM: Calling `object_validate(object_id="obj_3")` -> returns validation success
|
|
10
|
+
LLM: Calling `object_resolve(object_id="obj_3")` -> returns fully compiled object
|
|
11
|
+
```
|
|
12
|
+
This demonstrates updating properties incrementally across dialogue turns.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"filter_session_state": {
|
|
4
|
+
"_type": "adapter",
|
|
5
|
+
"name": "memory"
|
|
6
|
+
},
|
|
7
|
+
"filter_persistent_state": {
|
|
8
|
+
"global": {
|
|
9
|
+
"_type": "adapter",
|
|
10
|
+
"name": "memory"
|
|
11
|
+
},
|
|
12
|
+
"user": {
|
|
13
|
+
"_type": "adapter",
|
|
14
|
+
"name": "memory"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"object_session_state": {
|
|
18
|
+
"_type": "adapter",
|
|
19
|
+
"name": "memory"
|
|
20
|
+
},
|
|
21
|
+
"object_persistent_state": {
|
|
22
|
+
"global": {
|
|
23
|
+
"_type": "adapter",
|
|
24
|
+
"name": "memory"
|
|
25
|
+
},
|
|
26
|
+
"user": {
|
|
27
|
+
"_type": "adapter",
|
|
28
|
+
"name": "memory"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dictionary_state": {
|
|
32
|
+
"_type": "adapter",
|
|
33
|
+
"name": "memory"
|
|
34
|
+
},
|
|
35
|
+
"dictionary_resolver": {
|
|
36
|
+
"_type": "adapter",
|
|
37
|
+
"name": "memory"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import json
|
|
3
|
+
import traceback
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import duckdb
|
|
8
|
+
except ImportError:
|
|
9
|
+
# Print error to stderr so the TS process knows imports failed
|
|
10
|
+
print("Error: Missing 'pandas' or 'duckdb' library.", file=sys.stderr)
|
|
11
|
+
sys.exit(1)
|
|
12
|
+
|
|
13
|
+
# Keep references to loaded dataframes in local scope
|
|
14
|
+
dfs = {}
|
|
15
|
+
|
|
16
|
+
def handle_load(params):
|
|
17
|
+
source_file = params.get("source_file")
|
|
18
|
+
df_name = params.get("dataframe_name", "df")
|
|
19
|
+
|
|
20
|
+
if not source_file:
|
|
21
|
+
return {"success": False, "error": "Missing 'source_file' parameter"}
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
if source_file.endswith(".csv"):
|
|
25
|
+
df = pd.read_csv(source_file)
|
|
26
|
+
elif source_file.endswith(".parquet"):
|
|
27
|
+
df = pd.read_parquet(source_file)
|
|
28
|
+
elif source_file.endswith(".json") or source_file.endswith(".jsonl"):
|
|
29
|
+
df = pd.read_json(source_file, lines=source_file.endswith(".jsonl"))
|
|
30
|
+
else:
|
|
31
|
+
return {"success": False, "error": f"Unsupported file format: {source_file}"}
|
|
32
|
+
|
|
33
|
+
dfs[df_name] = df
|
|
34
|
+
# Register the DataFrame as a view in DuckDB
|
|
35
|
+
duckdb.register(df_name, df)
|
|
36
|
+
return {"success": True, "rows": len(df)}
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return {"success": False, "error": str(e), "traceback": traceback.format_exc()}
|
|
39
|
+
|
|
40
|
+
def handle_query(params):
|
|
41
|
+
sql = params.get("sql")
|
|
42
|
+
params_list = params.get("params", [])
|
|
43
|
+
if not sql:
|
|
44
|
+
return {"success": False, "error": "Missing 'sql' parameter"}
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
# Run DuckDB SQL query directly over the registered Pandas DataFrames
|
|
48
|
+
res = duckdb.execute(sql, params_list).df()
|
|
49
|
+
# Convert to records JSON representation
|
|
50
|
+
records = res.to_dict(orient="records")
|
|
51
|
+
# Handle nan/inf float serialization issues by converting to None
|
|
52
|
+
cleaned = json.loads(json.dumps(records, default=str))
|
|
53
|
+
return {"success": True, "data": cleaned}
|
|
54
|
+
except Exception as e:
|
|
55
|
+
return {"success": False, "error": str(e), "traceback": traceback.format_exc()}
|
|
56
|
+
|
|
57
|
+
def main():
|
|
58
|
+
# Loop reading JSON commands from stdin
|
|
59
|
+
while True:
|
|
60
|
+
line = sys.stdin.readline()
|
|
61
|
+
if not line:
|
|
62
|
+
break
|
|
63
|
+
if not line.strip():
|
|
64
|
+
continue
|
|
65
|
+
try:
|
|
66
|
+
cmd = json.loads(line)
|
|
67
|
+
action = cmd.get("action")
|
|
68
|
+
|
|
69
|
+
if action == "load":
|
|
70
|
+
res = handle_load(cmd)
|
|
71
|
+
elif action == "query":
|
|
72
|
+
res = handle_query(cmd)
|
|
73
|
+
elif action == "ping":
|
|
74
|
+
res = {"success": True, "pong": True}
|
|
75
|
+
else:
|
|
76
|
+
res = {"success": False, "error": f"Unknown action: {action}"}
|
|
77
|
+
|
|
78
|
+
if "id" in cmd:
|
|
79
|
+
res["id"] = cmd["id"]
|
|
80
|
+
|
|
81
|
+
print(json.dumps(res))
|
|
82
|
+
sys.stdout.flush()
|
|
83
|
+
except Exception as e:
|
|
84
|
+
err_res = {"success": False, "error": f"Malformed input: {str(e)}"}
|
|
85
|
+
print(json.dumps(err_res))
|
|
86
|
+
sys.stdout.flush()
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
main()
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
|