@anvia/cli 1.1.1 → 1.3.0
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/README.md +48 -0
- package/dist/chunk-ERCP4EIZ.js +607 -0
- package/dist/chunk-ERCP4EIZ.js.map +1 -0
- package/dist/cli.js +287 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +27 -3
- package/dist/skills/anvia-agent/SKILL.md +59 -0
- package/dist/skills/anvia-agent/references/agent-options.md +83 -0
- package/dist/skills/anvia-agent/references/providers.md +22 -0
- package/dist/skills/anvia-agent/references/teams.md +84 -0
- package/dist/skills/anvia-agent/references/tools.md +71 -0
- package/dist/skills/anvia-agent/scripts/check-agent.sh +99 -0
- package/dist/skills/anvia-channels/SKILL.md +59 -0
- package/dist/skills/anvia-channels/references/adapters.md +53 -0
- package/dist/skills/anvia-channels/references/channel-agent.md +52 -0
- package/dist/skills/anvia-channels/references/delivery.md +53 -0
- package/dist/skills/anvia-channels/scripts/check-channels.sh +79 -0
- package/dist/skills/anvia-chat/SKILL.md +61 -0
- package/dist/skills/anvia-chat/references/react-ui.md +104 -0
- package/dist/skills/anvia-chat/references/server-protocol.md +53 -0
- package/dist/skills/anvia-chat/references/transports-state.md +71 -0
- package/dist/skills/anvia-chat/scripts/check-chat-boundary.sh +70 -0
- package/dist/skills/anvia-evals/SKILL.md +51 -0
- package/dist/skills/anvia-evals/references/judges.md +61 -0
- package/dist/skills/anvia-evals/references/metrics.md +45 -0
- package/dist/skills/anvia-evals/references/running.md +62 -0
- package/dist/skills/anvia-evals/scripts/check-evals.sh +73 -0
- package/dist/skills/anvia-mcp/SKILL.md +49 -0
- package/dist/skills/anvia-mcp/references/clients.md +73 -0
- package/dist/skills/anvia-mcp/references/safety.md +25 -0
- package/dist/skills/anvia-mcp/scripts/check-mcp.sh +73 -0
- package/dist/skills/anvia-pipeline/SKILL.md +52 -0
- package/dist/skills/anvia-pipeline/references/agents-extract.md +62 -0
- package/dist/skills/anvia-pipeline/references/steps-compose.md +63 -0
- package/dist/skills/anvia-pipeline/scripts/check-pipeline.sh +69 -0
- package/dist/skills/anvia-rag/SKILL.md +50 -0
- package/dist/skills/anvia-rag/references/graph-rag.md +115 -0
- package/dist/skills/anvia-rag/references/pipeline.md +81 -0
- package/dist/skills/anvia-rag/references/rag-tool.md +43 -0
- package/dist/skills/anvia-rag/references/stores.md +68 -0
- package/dist/skills/anvia-rag/scripts/check-rag.sh +75 -0
- package/dist/skills/anvia-studio/SKILL.md +45 -0
- package/dist/skills/anvia-studio/references/inspect.md +33 -0
- package/dist/skills/anvia-studio/references/observe.md +34 -0
- package/dist/skills/anvia-studio/references/serve.md +65 -0
- package/dist/skills/anvia-studio/scripts/check-studio.sh +59 -0
- package/dist/skills/release-notes/SKILL.md +18 -0
- package/dist/skills/release-notes/references/style.md +6 -0
- package/dist/skills/release-notes/scripts/draft.sh +22 -0
- package/package.json +3 -3
- package/dist/chunk-TE2ODJOV.js +0 -135
- package/dist/chunk-TE2ODJOV.js.map +0 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Graph RAG
|
|
2
|
+
|
|
3
|
+
Use a knowledge graph when the questions are about connections (which incidents
|
|
4
|
+
affect which products, who owns what) — vectors find similar text, graphs
|
|
5
|
+
traverse relationships. The two compose: managed ingestion embeds chunks for a
|
|
6
|
+
vector store while extracting facts for the graph.
|
|
7
|
+
|
|
8
|
+
Core primitives live in `@anvia/graph`; provisioning, persistence, and queries
|
|
9
|
+
belong to adapters (`@anvia/neo4j`, `@anvia/memgraph`).
|
|
10
|
+
|
|
11
|
+
## Schema first
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { defineGraphSchema } from "@anvia/graph";
|
|
15
|
+
|
|
16
|
+
const schema = defineGraphSchema({
|
|
17
|
+
nodes: {
|
|
18
|
+
Product: {
|
|
19
|
+
description: "A product or service.",
|
|
20
|
+
identity: ["id"],
|
|
21
|
+
properties: z.strictObject({ id: z.string(), name: z.string() }),
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
relationships: {
|
|
25
|
+
AFFECTS: {
|
|
26
|
+
description: "An incident affects a product.",
|
|
27
|
+
from: "Incident",
|
|
28
|
+
to: "Product",
|
|
29
|
+
properties: z.strictObject({ severity: z.enum(["low", "medium", "high"]) }),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Design the schema from the questions, not the source tables. `identity` fields
|
|
36
|
+
are how facts merge — wrong identity modeling duplicates entities forever.
|
|
37
|
+
|
|
38
|
+
## Ingestion
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { ingestGraphText } from "@anvia/graph";
|
|
42
|
+
|
|
43
|
+
const ingestion = await ingestGraphText({
|
|
44
|
+
graph,
|
|
45
|
+
document: { id: "incident-42", text, metadata: { tenant: "acme" } },
|
|
46
|
+
extractionModel,
|
|
47
|
+
embeddingModel,
|
|
48
|
+
chunking: {
|
|
49
|
+
strategy: "recursive",
|
|
50
|
+
maxSize: 1_000,
|
|
51
|
+
overlap: 100,
|
|
52
|
+
separators: ["\n\n", "\n", " "],
|
|
53
|
+
},
|
|
54
|
+
conflict: "error",
|
|
55
|
+
orphanEntities: "delete",
|
|
56
|
+
factConflicts: {
|
|
57
|
+
entity: { properties: { summary: "prefer-longest", aliases: "union", confidence: "max" } },
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Managed graphs accept the same raw-text shape as vector ingestion, so reuse
|
|
63
|
+
your chunking settings. `ingestGraphDocuments()` batches; `ingestGraphTextToStores()`
|
|
64
|
+
writes graph + vector store in one call.
|
|
65
|
+
- Two conflict policies: `factConflicts` (disagreements between chunks, rejected
|
|
66
|
+
by default — strategies like `prefer-longest`, `union`, `max`), and `conflict`
|
|
67
|
+
(collisions with already-stored facts). Read both errors; they carry fact
|
|
68
|
+
type, stable key, identity, and source chunk IDs.
|
|
69
|
+
- Graph and vector writes are separate transactions. On partial failure
|
|
70
|
+
(`GraphIngestionStageError`) reconcile from the returned `receipt` — persist
|
|
71
|
+
ingestion status and retry the incomplete stage yourself.
|
|
72
|
+
- Reuse `ingestion.vectorDocuments` for the vector upsert instead of
|
|
73
|
+
re-embedding. Existing graph registrations are read-only ingestion targets.
|
|
74
|
+
|
|
75
|
+
## Search tool
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { createGraphSearchTool } from "@anvia/graph";
|
|
79
|
+
|
|
80
|
+
const searchGraph = createGraphSearchTool({
|
|
81
|
+
name: "search_graph",
|
|
82
|
+
description: "Search connected entities and supporting evidence.", // required — no default
|
|
83
|
+
graph,
|
|
84
|
+
model: embeddingModel,
|
|
85
|
+
search: { type: "vector", seeds: ["entities"], topK: 8 },
|
|
86
|
+
traversal: {
|
|
87
|
+
relationships: ["AFFECTS"],
|
|
88
|
+
direction: "both",
|
|
89
|
+
maxDepth: 2,
|
|
90
|
+
maxNodes: 40,
|
|
91
|
+
maxRelationships: 80,
|
|
92
|
+
},
|
|
93
|
+
evidence: { type: "chunks", maxChunks: 12 },
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `search` has two modes: `{ type: "vector", seeds, topK }` and
|
|
98
|
+
`{ type: "hybrid", seeds, topK, candidatesPerSeed, rrfK }` — hybrid fuses
|
|
99
|
+
vector similarity with per-seed graph matches. Prefer it when seed names
|
|
100
|
+
alone miss paraphrased entities.
|
|
101
|
+
- Bound everything: `maxDepth`, `maxNodes`, `maxRelationships`, `maxChunks`.
|
|
102
|
+
Unbounded traversal is the graph equivalent of `topK: 1000`.
|
|
103
|
+
- Evidence modes depend on the registration: managed graphs hydrate stored
|
|
104
|
+
chunks, existing-graph registrations use `{ type: "none" }`.
|
|
105
|
+
- One search tool per question shape, as with vector tools — graph and vector
|
|
106
|
+
tools coexist on the same agent when some questions are relational and some
|
|
107
|
+
are textual.
|
|
108
|
+
|
|
109
|
+
## Exploration
|
|
110
|
+
|
|
111
|
+
Adapters implementing `GraphExplorer` expose bounded `overview` / `expand`
|
|
112
|
+
views for visualization (Studio's graph explorer, `@anvia/react`
|
|
113
|
+
`useGraphExplorer`). Explorer IDs are opaque and provider-specific — follow-up
|
|
114
|
+
expansion only, never persistence. Adapters cap requests, return truncation
|
|
115
|
+
metadata, and omit stored embeddings and reserved `__anvia_*` properties.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Chunk and Embed Pipeline
|
|
2
|
+
|
|
3
|
+
## Chunk
|
|
4
|
+
|
|
5
|
+
`chunkText` from `@anvia/core/documents` splits raw text before embedding. Keep
|
|
6
|
+
chunk ids stable and traceable to the source:
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { chunkText } from "@anvia/core/documents";
|
|
10
|
+
import type { Document } from "@anvia/core/completion";
|
|
11
|
+
|
|
12
|
+
const chunks = chunkText({
|
|
13
|
+
text,
|
|
14
|
+
strategy: "recursive",
|
|
15
|
+
maxSize: 80,
|
|
16
|
+
overlap: 10,
|
|
17
|
+
separators: ["\n\n", "\n", " "],
|
|
18
|
+
}).map((chunk): Document => ({
|
|
19
|
+
id: `${path}#chunk=${chunk.index}`,
|
|
20
|
+
text: chunk.text,
|
|
21
|
+
additionalProps: {
|
|
22
|
+
source: path,
|
|
23
|
+
mediaType: "text/plain",
|
|
24
|
+
chunkIndex: String(chunk.index),
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Small overlap preserves cross-boundary context. Record provenance
|
|
30
|
+
(`source`, `mediaType`, `chunkIndex`) in `additionalProps` — retrieval results
|
|
31
|
+
are only as citable as the metadata you store.
|
|
32
|
+
|
|
33
|
+
## Embed
|
|
34
|
+
|
|
35
|
+
`embedDocuments` from `@anvia/core/embeddings` maps domain objects to embedded
|
|
36
|
+
documents with explicit selectors. The same model instance must embed documents
|
|
37
|
+
and queries:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { embedDocuments } from "@anvia/core/embeddings";
|
|
41
|
+
import { loadTransformersEmbeddingModel } from "@anvia/transformers";
|
|
42
|
+
|
|
43
|
+
const model = await loadTransformersEmbeddingModel({ modelId: "Xenova/all-MiniLM-L6-v2" });
|
|
44
|
+
const { documents: embedded } = await embedDocuments({
|
|
45
|
+
model,
|
|
46
|
+
documents: reports,
|
|
47
|
+
id: (report) => report.id,
|
|
48
|
+
content: (report) => report.text,
|
|
49
|
+
metadata: (report) => ({ desk: report.desk, priority: report.priority }),
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `id` must be stable across re-index runs or upserts will duplicate.
|
|
54
|
+
- `content` is what gets embedded — keep titles with bodies, drop boilerplate.
|
|
55
|
+
- `metadata` carries everything you will filter or display on (`desk`,
|
|
56
|
+
`priority`, `source`). You cannot filter on what you did not store.
|
|
57
|
+
- Custom models implement `EmbeddingModel` (`provider`, `modelId`,
|
|
58
|
+
`dimensions`, `embedTexts`). Production embeddings can also come from
|
|
59
|
+
provider packages (e.g. Mistral) — same `embedDocuments` contract.
|
|
60
|
+
|
|
61
|
+
## Retrieve
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { InMemoryVectorStore, retrieveDocuments, vectorFilter } from "@anvia/core/vector-store";
|
|
65
|
+
|
|
66
|
+
const store = InMemoryVectorStore.fromDocuments({
|
|
67
|
+
documents: embedded,
|
|
68
|
+
index: { type: "lsh", numTables: 8, numHyperplanes: 1, seed: 11 }, // opt-in for scale
|
|
69
|
+
});
|
|
70
|
+
const results = await retrieveDocuments({
|
|
71
|
+
store,
|
|
72
|
+
model,
|
|
73
|
+
query: "earnings risk remains elevated",
|
|
74
|
+
topK: 3,
|
|
75
|
+
filter: vectorFilter.and(vectorFilter.eq("desk", "markets"), vectorFilter.gt("priority", 2)),
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Start with exact retrieval (`topK`, metadata filters) before adding approximate
|
|
80
|
+
indexes — LSH trades recall for speed and needs a fixed `seed` for reproducible
|
|
81
|
+
evals.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# RAG Search Tool
|
|
2
|
+
|
|
3
|
+
Give the agent retrieval as a tool, not as pasted context. `createVectorSearchTool`
|
|
4
|
+
from `@anvia/core/vector-store` wraps a store + embedding model with a routing
|
|
5
|
+
description and `topK`:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Agent } from "@anvia/core/agent";
|
|
9
|
+
import { createVectorSearchTool } from "@anvia/core/vector-store";
|
|
10
|
+
|
|
11
|
+
const searchRunbooks = createVectorSearchTool({
|
|
12
|
+
store,
|
|
13
|
+
model: embeddingModel,
|
|
14
|
+
name: "search_runbooks",
|
|
15
|
+
description: "Search incident runbooks for relevant operational guidance.",
|
|
16
|
+
topK: 2,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const agent = new Agent({
|
|
20
|
+
id: "agent",
|
|
21
|
+
model: agentModel,
|
|
22
|
+
instructions: "Use the runbook search tool before answering incident questions.",
|
|
23
|
+
maxTurns: 2,
|
|
24
|
+
tools: [searchRunbooks],
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
|
|
30
|
+
- The tool `description` is routing text: say when to call it, not how vectors
|
|
31
|
+
work. It is optional (a generic default exists), but always set it — the
|
|
32
|
+
default cannot route when several corpora exist. One tool per corpus with a
|
|
33
|
+
distinct description beats one generic `search_docs` tool.
|
|
34
|
+
- Keep `topK` small (2–5). The agent reads what the tool returns — large `topK`
|
|
35
|
+
burns context and degrades answers.
|
|
36
|
+
- Instruct the agent to call search _before_ answering, and to cite `source`
|
|
37
|
+
metadata from the results. Retrieval without citation requirements produces
|
|
38
|
+
confident paraphrases of nothing.
|
|
39
|
+
- For static context that always applies (system rules, tiny glossaries), use
|
|
40
|
+
`Agent.context` (`Document` / `VectorContext`) instead of a tool — tools are
|
|
41
|
+
for corpora too large to fit the prompt.
|
|
42
|
+
- Close vector clients (`vectorClient.close()`) when the process ends;
|
|
43
|
+
in-memory stores need no lifecycle.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Vector Store Selection
|
|
2
|
+
|
|
3
|
+
Core contracts live in `@anvia/core/vector-store`
|
|
4
|
+
(`InMemoryVectorStore`, `retrieveDocuments`, `vectorFilter`,
|
|
5
|
+
`createVectorSearchTool`). Durable backends live in adapter packages:
|
|
6
|
+
|
|
7
|
+
| Package | Backend | Best for |
|
|
8
|
+
| ----------------- | -------- | ----------------------------------------- |
|
|
9
|
+
| `@anvia/chroma` | Chroma | local dev with persistence |
|
|
10
|
+
| `@anvia/lancedb` | LanceDB | embedded local persistence, no server |
|
|
11
|
+
| `@anvia/qdrant` | Qdrant | self-hosted production |
|
|
12
|
+
| `@anvia/pgvector` | pgvector | teams already on Postgres |
|
|
13
|
+
| `@anvia/milvus` | Milvus | large-scale self-hosted |
|
|
14
|
+
| `@anvia/pinecone` | Pinecone | managed production |
|
|
15
|
+
| `@anvia/redis` | Redis | existing Redis infra, hybrid search needs |
|
|
16
|
+
| `@anvia/weaviate` | Weaviate | managed / hybrid search features |
|
|
17
|
+
|
|
18
|
+
For connected-entity questions (which incidents affect which products), add a
|
|
19
|
+
knowledge graph alongside the vector store — see `graph-rag.md`. Vectors find
|
|
20
|
+
similar text; graphs traverse relationships.
|
|
21
|
+
|
|
22
|
+
## Hybrid (dense + sparse) retrieval
|
|
23
|
+
|
|
24
|
+
Keyword-ish recall problems ("error 0x8007", exact identifiers) are a sparse
|
|
25
|
+
retrieval problem. `retrieveDocuments` has a hybrid overload — pass
|
|
26
|
+
`models: { dense, sparse }` instead of `model`, and the store's `searchHybrid`
|
|
27
|
+
fuses both rankings (`fusion` controls the strategy):
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { embedSparseQuery } from "@anvia/core/embeddings"; // SparseEmbeddingModel
|
|
31
|
+
// embedSparseTexts embeds documents' sparse side at ingestion
|
|
32
|
+
|
|
33
|
+
const results = await retrieveDocuments({
|
|
34
|
+
store: hybridStore, // e.g. HybridVectorStore or a backend supporting searchHybrid
|
|
35
|
+
query,
|
|
36
|
+
models: { dense: embeddingModel, sparse: sparseModel },
|
|
37
|
+
topK: 5,
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Reach for hybrid when plain dense search misses exact tokens; keep pure dense
|
|
42
|
+
when it does not — hybrid adds an ingestion and store-compatibility cost.
|
|
43
|
+
|
|
44
|
+
## Client store pattern
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { ChromaVectorClient } from "@anvia/chroma";
|
|
48
|
+
|
|
49
|
+
const vectorClient = new ChromaVectorClient();
|
|
50
|
+
const store = vectorClient.vectorStore<Runbook>({
|
|
51
|
+
collectionName: "anvia_runbooks",
|
|
52
|
+
dimensions: 384, // must match the embedding model output
|
|
53
|
+
});
|
|
54
|
+
await store.ensure();
|
|
55
|
+
await store.upsert({ documents: embedded });
|
|
56
|
+
// ... retrieve ...
|
|
57
|
+
await vectorClient.close();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- `dimensions` must match the embedding model — a 384-dim MiniLM index queried
|
|
63
|
+
with another model's vectors fails silently on quality, not loudly on types.
|
|
64
|
+
- One model instance (or at least one model id) per collection. Never mix
|
|
65
|
+
embedding models in the same collection.
|
|
66
|
+
- `ensure()` before `upsert`; `close()` clients when the process ends.
|
|
67
|
+
- Start with `InMemoryVectorStore.fromDocuments` for spikes and tests, then
|
|
68
|
+
migrate to a client store — the `retrieveDocuments` call does not change.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Heuristic checks for Anvia retrieval/RAG code in an app directory.
|
|
3
|
+
# Usage: sh scripts/check-rag.sh [--dir <app-root>]
|
|
4
|
+
# Fails with a list of violations; passes silently with "rag OK".
|
|
5
|
+
|
|
6
|
+
DIR="."
|
|
7
|
+
if [ "$1" = "--dir" ] && [ -n "$2" ]; then
|
|
8
|
+
DIR="$2"
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
ROOTS_FOUND=0
|
|
12
|
+
for root in "$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server"; do
|
|
13
|
+
if [ -d "$root" ]; then
|
|
14
|
+
ROOTS_FOUND=1
|
|
15
|
+
break
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
if [ "$ROOTS_FOUND" -eq 0 ]; then
|
|
19
|
+
echo "ERROR: no src/app/lib/server directory under '$DIR' — nothing was checked."
|
|
20
|
+
echo "Run from the app root or pass --dir <app-root>."
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
fail=0
|
|
25
|
+
violation() {
|
|
26
|
+
echo "VIOLATION: $1"
|
|
27
|
+
fail=1
|
|
28
|
+
}
|
|
29
|
+
warning() {
|
|
30
|
+
echo "WARNING: $1"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
FILES=$(grep -rln --include='*.ts' \
|
|
34
|
+
-e 'embedDocuments' -e 'retrieveDocuments' -e 'createVectorSearchTool' -e 'createGraphSearchTool' -e 'vectorStore' \
|
|
35
|
+
"$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server" 2>/dev/null | sort -u)
|
|
36
|
+
|
|
37
|
+
if [ -z "$FILES" ]; then
|
|
38
|
+
echo "rag OK (no retrieval code found)"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 1. embedDocuments needs explicit id/content/metadata selectors.
|
|
43
|
+
if echo "$FILES" | xargs grep -l 'embedDocuments' 2>/dev/null | grep -q .; then
|
|
44
|
+
echo "$FILES" | xargs grep -h -A8 'embedDocuments(' 2>/dev/null | grep -q 'content:' || {
|
|
45
|
+
violation "embedDocuments without content selector (see references/pipeline.md)."
|
|
46
|
+
}
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# 2. Client vector stores need dimensions matching the embedding model.
|
|
50
|
+
if echo "$FILES" | xargs grep -l 'vectorStore' 2>/dev/null | grep -q .; then
|
|
51
|
+
echo "$FILES" | xargs grep -h -A5 'vectorStore' 2>/dev/null | grep -q 'dimensions' || {
|
|
52
|
+
violation "vectorStore without dimensions (see references/stores.md)."
|
|
53
|
+
}
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# 3. One search tool per corpus needs a routing description (warning: a generic default exists).
|
|
57
|
+
if echo "$FILES" | xargs grep -l 'createVectorSearchTool' 2>/dev/null | grep -q .; then
|
|
58
|
+
echo "$FILES" | xargs grep -h -A7 'createVectorSearchTool(' 2>/dev/null | grep -q 'description' || {
|
|
59
|
+
warning "createVectorSearchTool without description falls back to a generic default (see references/rag-tool.md)."
|
|
60
|
+
}
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# 4. Graph search tools have no default description — one is required.
|
|
64
|
+
if echo "$FILES" | xargs grep -l 'createGraphSearchTool' 2>/dev/null | grep -q .; then
|
|
65
|
+
echo "$FILES" | xargs grep -h -A7 'createGraphSearchTool(' 2>/dev/null | grep -q 'description' || {
|
|
66
|
+
violation "createGraphSearchTool without description (see references/graph-rag.md)."
|
|
67
|
+
}
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [ "$fail" -eq 0 ]; then
|
|
71
|
+
echo "rag OK"
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
echo "See skills/anvia-rag/references/ for fixes."
|
|
75
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: anvia-studio
|
|
3
|
+
description: Run and inspect Anvia agents in Studio — local serving, playground, traces, approvals, sessions, evals, and observability.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Anvia Studio Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when the user wants to run agents locally and see what happens:
|
|
9
|
+
serving agents/pipelines over HTTP, using the playground, inspecting traces and
|
|
10
|
+
sessions, exercising tool approvals, running eval suites, or wiring
|
|
11
|
+
observability.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
1. Serve the agents (`references/serve.md`) — smallest `Studio` that runs.
|
|
16
|
+
2. Debug through the inspectors (`references/inspect.md`) — playground first,
|
|
17
|
+
then traces, approvals, sessions.
|
|
18
|
+
3. Wire observability (`references/observe.md`) — only when runs must be
|
|
19
|
+
auditable or production-like.
|
|
20
|
+
4. Run `scripts/check-studio.sh` from the app root before claiming done.
|
|
21
|
+
|
|
22
|
+
## Minimal slice
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { Agent } from "@anvia/core";
|
|
26
|
+
import { OpenAIClient } from "@anvia/openai";
|
|
27
|
+
import { Studio } from "@anvia/studio";
|
|
28
|
+
|
|
29
|
+
const client = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY });
|
|
30
|
+
const agent = new Agent({
|
|
31
|
+
id: "support",
|
|
32
|
+
model: client.completionModel({ modelId: "gpt-6-astra", api: "responses" }),
|
|
33
|
+
name: "Support",
|
|
34
|
+
description: "Answers support questions.",
|
|
35
|
+
instructions: "Answer support questions clearly.",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await new Studio([agent]).serve({ port: 4021 });
|
|
39
|
+
// Open http://localhost:4021/ui/playground
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Output
|
|
43
|
+
|
|
44
|
+
Studio is a development surface, not production hosting. Point to the relevant
|
|
45
|
+
reference file instead of pasting its contents into chat.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Inspectors
|
|
2
|
+
|
|
3
|
+
Debug in this order: playground → traces → approvals → sessions. Each surface
|
|
4
|
+
answers a different question.
|
|
5
|
+
|
|
6
|
+
- **Playground** — chat with a registered agent or team, switch models per run
|
|
7
|
+
(Studio can expose a shared multi-provider catalog with per-agent allow
|
|
8
|
+
lists), restore control choices from session metadata. First stop for
|
|
9
|
+
"what does it do?".
|
|
10
|
+
- **Trace browser + session logs** — what actually happened: messages, tool
|
|
11
|
+
activity, usage, realtime log stream. First stop for "why did it do that?".
|
|
12
|
+
- **Tool approval workflows** — exercise `requiresApproval` tools and question
|
|
13
|
+
prompts end to end; approvals resolve through the same interaction responses
|
|
14
|
+
the API uses.
|
|
15
|
+
- **Eval suite runner** — run registered `runEvalSuite` configurations from the
|
|
16
|
+
UI. Keep the suite definitions in code (see the `anvia-evals` skill); Studio
|
|
17
|
+
runs them, it does not own them.
|
|
18
|
+
- **Direct tool + MCP inspectors** — invoke tools by hand, including MCP tools.
|
|
19
|
+
Studio reads MCP provenance from `Agent.mcpServers`, so `McpClient`
|
|
20
|
+
`tools.prefix` values stay visible here.
|
|
21
|
+
- **Memory explorer** — users, conversations, messages, transcript steps from
|
|
22
|
+
the session store. Empty here plus working chat means the store is not the
|
|
23
|
+
one the agent writes to — check `stores.sessions`.
|
|
24
|
+
- **Pipelines** — graph, logs, run history, replay-from-history.
|
|
25
|
+
- **Knowledge** — static/dynamic context, dynamic tools, retrieval log. First
|
|
26
|
+
stop for "why didn't it retrieve?".
|
|
27
|
+
- **Status dashboard** — storage adapters, record counts, enabled capabilities.
|
|
28
|
+
First stop for "is anything even configured?".
|
|
29
|
+
- **Graphs** — bounded overview/expansion over registered `GraphExplorer`
|
|
30
|
+
graphs (`@anvia/neo4j`, `@anvia/memgraph`); no raw queries accepted.
|
|
31
|
+
|
|
32
|
+
Sandbox/browser registrations add loopback-only noVNC desktop views with explicit
|
|
33
|
+
takeover leases — local debugging conveniences, not an auth model.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Observability
|
|
2
|
+
|
|
3
|
+
Studio's own surfaces (realtime log stream, trace browser, status dashboard)
|
|
4
|
+
cover development. Production-like auditing needs the observability packages:
|
|
5
|
+
|
|
6
|
+
- `@anvia/langfuse` — tracing, eval reporting, scoring, prompt/dataset helpers.
|
|
7
|
+
- `@anvia/otel` — OpenTelemetry adapters.
|
|
8
|
+
- `@anvia/lens` — native Lens integration (see the cookbook's lens-native example).
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
- Close observability clients in Studio's `onShutdown` — they are caller-owned
|
|
13
|
+
resources and outlive runs:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
await new Studio([agent]).serve({
|
|
17
|
+
port: 4021,
|
|
18
|
+
shutdownTimeoutMs: 30_000,
|
|
19
|
+
onShutdown: async () => {
|
|
20
|
+
await Promise.all([lens.close(), langfuse.close(), otelSdk.shutdown()]); // otelSdk is your OTel SDK instance, not an Anvia export
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Redact before export: `createPiiRedactor` ships with both `@anvia/langfuse`
|
|
26
|
+
and `@anvia/lens`. Wrap exporters with it so customer data never reaches
|
|
27
|
+
trace payloads in the first place.
|
|
28
|
+
- For application logs alongside traces, `@anvia/logger` provides
|
|
29
|
+
`createConsoleLogger`, `createPinoLogger`, and `createLoggerObserver`.
|
|
30
|
+
- Report evals to observability (Langfuse eval reporting, trace refs via
|
|
31
|
+
`resolveEvalTraceRef`) when runs must be auditable — see the `anvia-evals`
|
|
32
|
+
skill's `references/running.md`.
|
|
33
|
+
- Never commit keys, private prompts, customer data, or trace payloads. Read
|
|
34
|
+
credentials from the environment at the boundary, same as provider keys.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Serving Agents
|
|
2
|
+
|
|
3
|
+
`@anvia/studio` serves local agents, pipelines, and agent teams over HTTP with
|
|
4
|
+
a browser UI. Register what you already built — Studio does not redefine agents.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { Agent, AgentTeam } from "@anvia/core/agent";
|
|
8
|
+
import { Studio } from "@anvia/studio";
|
|
9
|
+
|
|
10
|
+
const researcher = new Agent({ id: "researcher", model });
|
|
11
|
+
const team = new AgentTeam({ id: "research-team", model, members: [researcher] });
|
|
12
|
+
await new Studio([researcher, team]).serve({ port: 4021 });
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `serve({ port })` starts HTTP + UI; `.start()` is the non-awaited variant.
|
|
16
|
+
Without a port, Studio uses `RUNNER_PORT`, then falls back to `4021`.
|
|
17
|
+
- The playground lives at `/ui/playground`.
|
|
18
|
+
- A Studio with only teams opens its first team automatically; agent and team
|
|
19
|
+
IDs occupy separate namespaces.
|
|
20
|
+
|
|
21
|
+
## Sessions
|
|
22
|
+
|
|
23
|
+
Studio uses an in-memory store by default — sessions, traces, and run history
|
|
24
|
+
vanish on restart. Persist with SQLite:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { Studio, createSqliteSessionStore } from "@anvia/studio";
|
|
28
|
+
|
|
29
|
+
new Studio([agent], {
|
|
30
|
+
stores: { sessions: createSqliteSessionStore({ path: ".anvia/studio.sqlite" }) },
|
|
31
|
+
}).start();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
SQLite uses dedicated `anvia_studio_*` tables, so it can share an application
|
|
35
|
+
database without touching product tables.
|
|
36
|
+
|
|
37
|
+
## Lifecycle
|
|
38
|
+
|
|
39
|
+
- `serve()` handles `SIGINT`/`SIGTERM`: stops accepting work, aborts active
|
|
40
|
+
Agent and Pipeline runs, waits for cancellation observers, then runs
|
|
41
|
+
`onShutdown` — close observability clients and other caller-owned resources
|
|
42
|
+
there.
|
|
43
|
+
- `shutdown()` drains the same way for application-managed lifecycles;
|
|
44
|
+
`close()` aborts without waiting (compatibility only).
|
|
45
|
+
|
|
46
|
+
## Runs
|
|
47
|
+
|
|
48
|
+
The primary execution API is `POST /agents/:agentId/runs`: it streams JSONL
|
|
49
|
+
events and accepts interaction-resume bodies, so scripts and CI can drive an
|
|
50
|
+
agent without the browser UI.
|
|
51
|
+
|
|
52
|
+
## Teams
|
|
53
|
+
|
|
54
|
+
Team runs stream JSONL events (`POST /teams/:teamId/runs`), with steer, cancel,
|
|
55
|
+
and per-interaction resolve endpoints. Approvals and questions surface as cards
|
|
56
|
+
labeled with the requesting member; several members may block at once. Team
|
|
57
|
+
tasks are page-local and ephemeral — they never enter saved agent sessions.
|
|
58
|
+
|
|
59
|
+
## Rules
|
|
60
|
+
|
|
61
|
+
- Studio provides no authentication. Apply auth middleware to execution routes
|
|
62
|
+
before exposing it beyond localhost.
|
|
63
|
+
- Disconnecting a stream or shutting down cancels the run and its pending
|
|
64
|
+
interactions — completed runs leave the live registry (later control calls
|
|
65
|
+
404), and team runs are not resumable.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Heuristic checks for Anvia Studio code in an app directory.
|
|
3
|
+
# Usage: sh scripts/check-studio.sh [--dir <app-root>]
|
|
4
|
+
# Fails with a list of violations; passes silently with "studio OK".
|
|
5
|
+
|
|
6
|
+
DIR="."
|
|
7
|
+
if [ "$1" = "--dir" ] && [ -n "$2" ]; then
|
|
8
|
+
DIR="$2"
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
ROOTS_FOUND=0
|
|
12
|
+
for root in "$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server"; do
|
|
13
|
+
if [ -d "$root" ]; then
|
|
14
|
+
ROOTS_FOUND=1
|
|
15
|
+
break
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
if [ "$ROOTS_FOUND" -eq 0 ]; then
|
|
19
|
+
echo "ERROR: no src/app/lib/server directory under '$DIR' — nothing was checked."
|
|
20
|
+
echo "Run from the app root or pass --dir <app-root>."
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
fail=0
|
|
25
|
+
violation() {
|
|
26
|
+
echo "VIOLATION: $1"
|
|
27
|
+
fail=1
|
|
28
|
+
}
|
|
29
|
+
warning() {
|
|
30
|
+
echo "WARNING: $1"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
FILES=$(grep -rln --include='*.ts' \
|
|
34
|
+
-e 'new Studio(' -e '@anvia/studio' \
|
|
35
|
+
"$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server" 2>/dev/null | sort -u)
|
|
36
|
+
|
|
37
|
+
if [ -z "$FILES" ]; then
|
|
38
|
+
echo "studio OK (no Studio code found)"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 1. Studio must serve something.
|
|
43
|
+
if echo "$FILES" | xargs grep -h -e 'new Studio(' 2>/dev/null | grep -q -e 'new Studio(\s*\[\s*\]' -e 'new Studio(\s*)'; then
|
|
44
|
+
violation "new Studio with no registered agents/teams (see references/serve.md)."
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# 2. Observability clients are caller-owned: close them in onShutdown (warning only).
|
|
48
|
+
if echo "$FILES" | xargs grep -l -e '@anvia/langfuse' -e '@anvia/otel' -e '@anvia/lens' 2>/dev/null | grep -q .; then
|
|
49
|
+
echo "$FILES" | xargs grep -h -e 'onShutdown' 2>/dev/null | grep -q 'onShutdown' || {
|
|
50
|
+
warning "observability clients without onShutdown — they will leak on exit (see references/observe.md)."
|
|
51
|
+
}
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [ "$fail" -eq 0 ]; then
|
|
55
|
+
echo "studio OK"
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
echo "See skills/anvia-studio/references/ for fixes."
|
|
59
|
+
exit 1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-notes
|
|
3
|
+
description: Draft concise product release notes from a list of shipped changes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release Notes Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks for release notes, changelogs, launch notes, or a concise product update.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
1. Read `references/style.md`.
|
|
13
|
+
2. Run `scripts/draft.sh` with the shipped changes as one argument.
|
|
14
|
+
3. Rewrite the script output into final release notes for the requested audience.
|
|
15
|
+
|
|
16
|
+
## Output
|
|
17
|
+
|
|
18
|
+
Keep the final answer short, concrete, and grouped by user-visible capability.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Draft release-notes skeleton from a list of shipped changes (one argument,
|
|
3
|
+
# one change per line). The skill rewrites this skeleton into final notes.
|
|
4
|
+
|
|
5
|
+
changes="$1"
|
|
6
|
+
|
|
7
|
+
if [ -z "$changes" ]; then
|
|
8
|
+
echo "usage: draft.sh \"<shipped changes, one per line>\"" >&2
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
printf 'Draft release notes\n====================\n\n'
|
|
13
|
+
printf 'Summary: <one sentence covering the theme of these changes>\n\n'
|
|
14
|
+
printf 'Changes:\n'
|
|
15
|
+
printf '%s\n' "$changes" | while IFS= read -r line; do
|
|
16
|
+
if [ -n "$line" ]; then
|
|
17
|
+
case "$line" in
|
|
18
|
+
"- "*) line="${line#- }" ;;
|
|
19
|
+
esac
|
|
20
|
+
printf -- '- %s\n' "$line"
|
|
21
|
+
fi
|
|
22
|
+
done
|