From ce25d828345956a55079013a9721f014fbd98639 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:24:39 +0700 Subject: [PATCH 01/16] feat: add Pinecone and Milvus vector store adapters (#102) * feat: add Pinecone and Milvus vector store adapters Add @anvia/pinecone and @anvia/milvus packages following the existing adapter pattern (Chroma, PgVector, Qdrant). Both implement the VectorSearchIndex interface from @anvia/core with full filter translation, multi-embedding support, and asTool() integration. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * refactor: split vector-stores into flat file modules Split each monolithic src/index.ts (220-364 lines) into focused modules: - types.ts (client interfaces, config types) - store.ts (*VectorStore class) - search-index.ts (*VectorIndex class) - filters.ts (VectorFilter translation) - helpers.ts (serialization, parsing, ID generation) - index.ts (barrel re-exports) Applied to all 5 packages: chroma, qdrant, pgvector, pinecone, milvus. Public API surface unchanged -- all exports preserved via barrel. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * docs: add Pinecone and Milvus reference docs and changeset Add reference documentation for @anvia/pinecone and @anvia/milvus, register them in the reference coverage check, and create a minor changeset for both new packages. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * test: add unit tests for Pinecone and Milvus vector stores Add test suites matching the existing pattern (Chroma, Qdrant, PgVector). Tests cover connect, upsert, search, filter translation, reserved metadata rejection, and empty embeddings rejection. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .changeset/add-pinecone-milvus.md | 6 + apps/docs/content/docs/changelog/chroma.mdx | 7 + apps/docs/content/docs/changelog/core.mdx | 6 + .../docs/content/docs/changelog/fastembed.mdx | 7 + apps/docs/content/docs/changelog/index.mdx | 26 +- apps/docs/content/docs/changelog/logger.mdx | 7 + apps/docs/content/docs/changelog/meta.json | 2 + apps/docs/content/docs/changelog/milvus.mdx | 17 + apps/docs/content/docs/changelog/mistral.mdx | 7 + apps/docs/content/docs/changelog/openai.mdx | 7 + apps/docs/content/docs/changelog/otel.mdx | 7 + apps/docs/content/docs/changelog/pgvector.mdx | 7 + apps/docs/content/docs/changelog/pinecone.mdx | 17 + apps/docs/content/docs/changelog/qdrant.mdx | 7 + apps/docs/content/docs/changelog/sandbox.mdx | 7 + apps/docs/content/docs/changelog/studio.mdx | 7 + .../content/docs/changelog/transformers.mdx | 7 + .../docs/reference/integrations/chroma.mdx | 11 + .../docs/reference/integrations/milvus.mdx | 81 ++ .../docs/reference/integrations/pinecone.mdx | 102 ++ .../docs/scripts/check-reference-coverage.mjs | 2 + apps/docs/src/routeTree.gen.ts | 9 - packages/vector-stores/chroma/src/filters.ts | 20 + packages/vector-stores/chroma/src/helpers.ts | 103 ++ packages/vector-stores/chroma/src/index.ts | 228 +--- .../vector-stores/chroma/src/search-index.ts | 40 + packages/vector-stores/chroma/src/store.ts | 43 + packages/vector-stores/chroma/src/types.ts | 18 + packages/vector-stores/milvus/package.json | 43 + packages/vector-stores/milvus/src/filters.ts | 44 + packages/vector-stores/milvus/src/helpers.ts | 152 +++ packages/vector-stores/milvus/src/index.ts | 8 + .../vector-stores/milvus/src/search-index.ts | 43 + packages/vector-stores/milvus/src/store.ts | 41 + packages/vector-stores/milvus/src/types.ts | 22 + .../milvus/test/milvus-vector-store.test.ts | 196 ++++ packages/vector-stores/milvus/tsconfig.json | 7 + .../vector-stores/pgvector/src/filters.ts | 79 ++ .../vector-stores/pgvector/src/helpers.ts | 145 +++ packages/vector-stores/pgvector/src/index.ts | 373 +------ .../pgvector/src/search-index.ts | 63 ++ packages/vector-stores/pgvector/src/store.ts | 76 ++ packages/vector-stores/pgvector/src/types.ts | 21 + packages/vector-stores/pinecone/package.json | 43 + .../vector-stores/pinecone/src/filters.ts | 20 + .../vector-stores/pinecone/src/helpers.ts | 140 +++ packages/vector-stores/pinecone/src/index.ts | 10 + .../pinecone/src/search-index.ts | 40 + packages/vector-stores/pinecone/src/store.ts | 30 + packages/vector-stores/pinecone/src/types.ts | 28 + .../test/pinecone-vector-store.test.ts | 169 +++ packages/vector-stores/pinecone/tsconfig.json | 7 + packages/vector-stores/qdrant/src/filters.ts | 20 + packages/vector-stores/qdrant/src/helpers.ts | 154 +++ packages/vector-stores/qdrant/src/index.ts | 276 +---- .../vector-stores/qdrant/src/search-index.ts | 41 + packages/vector-stores/qdrant/src/store.ts | 42 + packages/vector-stores/qdrant/src/types.ts | 20 + pnpm-lock.yaml | 996 +++++++++++++++++- 59 files changed, 3281 insertions(+), 876 deletions(-) create mode 100644 .changeset/add-pinecone-milvus.md create mode 100644 apps/docs/content/docs/changelog/milvus.mdx create mode 100644 apps/docs/content/docs/changelog/pinecone.mdx create mode 100644 apps/docs/content/docs/reference/integrations/milvus.mdx create mode 100644 apps/docs/content/docs/reference/integrations/pinecone.mdx create mode 100644 packages/vector-stores/chroma/src/filters.ts create mode 100644 packages/vector-stores/chroma/src/helpers.ts create mode 100644 packages/vector-stores/chroma/src/search-index.ts create mode 100644 packages/vector-stores/chroma/src/store.ts create mode 100644 packages/vector-stores/chroma/src/types.ts create mode 100644 packages/vector-stores/milvus/package.json create mode 100644 packages/vector-stores/milvus/src/filters.ts create mode 100644 packages/vector-stores/milvus/src/helpers.ts create mode 100644 packages/vector-stores/milvus/src/index.ts create mode 100644 packages/vector-stores/milvus/src/search-index.ts create mode 100644 packages/vector-stores/milvus/src/store.ts create mode 100644 packages/vector-stores/milvus/src/types.ts create mode 100644 packages/vector-stores/milvus/test/milvus-vector-store.test.ts create mode 100644 packages/vector-stores/milvus/tsconfig.json create mode 100644 packages/vector-stores/pgvector/src/filters.ts create mode 100644 packages/vector-stores/pgvector/src/helpers.ts create mode 100644 packages/vector-stores/pgvector/src/search-index.ts create mode 100644 packages/vector-stores/pgvector/src/store.ts create mode 100644 packages/vector-stores/pgvector/src/types.ts create mode 100644 packages/vector-stores/pinecone/package.json create mode 100644 packages/vector-stores/pinecone/src/filters.ts create mode 100644 packages/vector-stores/pinecone/src/helpers.ts create mode 100644 packages/vector-stores/pinecone/src/index.ts create mode 100644 packages/vector-stores/pinecone/src/search-index.ts create mode 100644 packages/vector-stores/pinecone/src/store.ts create mode 100644 packages/vector-stores/pinecone/src/types.ts create mode 100644 packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts create mode 100644 packages/vector-stores/pinecone/tsconfig.json create mode 100644 packages/vector-stores/qdrant/src/filters.ts create mode 100644 packages/vector-stores/qdrant/src/helpers.ts create mode 100644 packages/vector-stores/qdrant/src/search-index.ts create mode 100644 packages/vector-stores/qdrant/src/store.ts create mode 100644 packages/vector-stores/qdrant/src/types.ts diff --git a/.changeset/add-pinecone-milvus.md b/.changeset/add-pinecone-milvus.md new file mode 100644 index 00000000..4e3bb24b --- /dev/null +++ b/.changeset/add-pinecone-milvus.md @@ -0,0 +1,6 @@ +--- +"@anvia/pinecone": minor +"@anvia/milvus": minor +--- + +Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/apps/docs/content/docs/changelog/chroma.mdx b/apps/docs/content/docs/changelog/chroma.mdx index 300b9ff9..bafa18c0 100644 --- a/apps/docs/content/docs/changelog/chroma.mdx +++ b/apps/docs/content/docs/changelog/chroma.mdx @@ -9,6 +9,13 @@ ChromaDB vector store adapter for Anvia. Source: [`packages/vector-stores/chroma/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/chroma/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/core.mdx b/apps/docs/content/docs/changelog/core.mdx index 921b4826..332edfca 100644 --- a/apps/docs/content/docs/changelog/core.mdx +++ b/apps/docs/content/docs/changelog/core.mdx @@ -9,6 +9,12 @@ Core runtime primitives for context-aware Anvia agents. Source: [`packages/core/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/core/CHANGELOG.md) +## 0.6.1 + +### Patch Changes + +- 2d039f6: Add ergonomic tool result message helpers and export `ToolContent` from the root entrypoint. + ## 0.6.0 ### Minor Changes diff --git a/apps/docs/content/docs/changelog/fastembed.mdx b/apps/docs/content/docs/changelog/fastembed.mdx index 856a1159..2f52c02c 100644 --- a/apps/docs/content/docs/changelog/fastembed.mdx +++ b/apps/docs/content/docs/changelog/fastembed.mdx @@ -9,6 +9,13 @@ FastEmbed embedding model adapter for Anvia. Source: [`packages/embeddings/fastembed/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/fastembed/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/index.mdx b/apps/docs/content/docs/changelog/index.mdx index d7e09ba1..870ac269 100644 --- a/apps/docs/content/docs/changelog/index.mdx +++ b/apps/docs/content/docs/changelog/index.mdx @@ -13,7 +13,7 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/core` | `0.6.0` | [View changelog](/docs/changelog/core) | +| `@anvia/core` | `0.6.1` | [View changelog](/docs/changelog/core) | ## Providers @@ -21,43 +21,45 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | --- | --- | --- | | `@anvia/anthropic` | `0.3.5` | [View changelog](/docs/changelog/anthropic) | | `@anvia/gemini` | `0.2.4` | [View changelog](/docs/changelog/gemini) | -| `@anvia/mistral` | `0.2.5` | [View changelog](/docs/changelog/mistral) | -| `@anvia/openai` | `0.3.6` | [View changelog](/docs/changelog/openai) | +| `@anvia/mistral` | `0.2.6` | [View changelog](/docs/changelog/mistral) | +| `@anvia/openai` | `0.3.7` | [View changelog](/docs/changelog/openai) | ## Embeddings | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/fastembed` | `0.2.4` | [View changelog](/docs/changelog/fastembed) | -| `@anvia/transformers` | `0.2.4` | [View changelog](/docs/changelog/transformers) | +| `@anvia/fastembed` | `0.2.5` | [View changelog](/docs/changelog/fastembed) | +| `@anvia/transformers` | `0.2.5` | [View changelog](/docs/changelog/transformers) | ## Vector Stores | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/chroma` | `0.2.4` | [View changelog](/docs/changelog/chroma) | -| `@anvia/pgvector` | `0.2.4` | [View changelog](/docs/changelog/pgvector) | -| `@anvia/qdrant` | `0.2.4` | [View changelog](/docs/changelog/qdrant) | +| `@anvia/chroma` | `0.2.5` | [View changelog](/docs/changelog/chroma) | +| `@anvia/milvus` | `0.2.5` | [View changelog](/docs/changelog/milvus) | +| `@anvia/pgvector` | `0.2.5` | [View changelog](/docs/changelog/pgvector) | +| `@anvia/pinecone` | `0.2.5` | [View changelog](/docs/changelog/pinecone) | +| `@anvia/qdrant` | `0.2.5` | [View changelog](/docs/changelog/qdrant) | ## Logger | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/logger` | `0.3.5` | [View changelog](/docs/changelog/logger) | +| `@anvia/logger` | `0.3.6` | [View changelog](/docs/changelog/logger) | ## Observability | Package | Current version | Release notes | | --- | --- | --- | | `@anvia/langfuse` | `0.2.3` | [View changelog](/docs/changelog/langfuse) | -| `@anvia/otel` | `0.2.4` | [View changelog](/docs/changelog/otel) | +| `@anvia/otel` | `0.2.5` | [View changelog](/docs/changelog/otel) | ## Tools | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/sandbox` | `0.2.1` | [View changelog](/docs/changelog/sandbox) | -| `@anvia/studio` | `0.5.10` | [View changelog](/docs/changelog/studio) | +| `@anvia/sandbox` | `0.2.2` | [View changelog](/docs/changelog/sandbox) | +| `@anvia/studio` | `0.5.11` | [View changelog](/docs/changelog/studio) | ## React diff --git a/apps/docs/content/docs/changelog/logger.mdx b/apps/docs/content/docs/changelog/logger.mdx index 09b6c155..025773e4 100644 --- a/apps/docs/content/docs/changelog/logger.mdx +++ b/apps/docs/content/docs/changelog/logger.mdx @@ -9,6 +9,13 @@ Structured logger adapters for Anvia. Source: [`packages/logger/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/logger/CHANGELOG.md) +## 0.3.6 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.3.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/meta.json b/apps/docs/content/docs/changelog/meta.json index 7cc6ecba..0edc4162 100644 --- a/apps/docs/content/docs/changelog/meta.json +++ b/apps/docs/content/docs/changelog/meta.json @@ -19,7 +19,9 @@ "transformers", "---Vector Stores---", "chroma", + "milvus", "pgvector", + "pinecone", "qdrant", "---Logger---", "logger", diff --git a/apps/docs/content/docs/changelog/milvus.mdx b/apps/docs/content/docs/changelog/milvus.mdx new file mode 100644 index 00000000..48c82f41 --- /dev/null +++ b/apps/docs/content/docs/changelog/milvus.mdx @@ -0,0 +1,17 @@ +--- +title: "@anvia/milvus" +description: "Release notes for @anvia/milvus." +--- + +# `@anvia/milvus` + +Milvus vector store adapter for Anvia. + +Source: [`packages/vector-stores/milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/milvus/CHANGELOG.md) + +## No release notes yet + +`@anvia/milvus` is public, but `packages/vector-stores/milvus/CHANGELOG.md` does not exist yet. + +Release notes will appear here after the next Changesets version bump creates the package changelog. + diff --git a/apps/docs/content/docs/changelog/mistral.mdx b/apps/docs/content/docs/changelog/mistral.mdx index ea7a34a7..b78015ff 100644 --- a/apps/docs/content/docs/changelog/mistral.mdx +++ b/apps/docs/content/docs/changelog/mistral.mdx @@ -9,6 +9,13 @@ Mistral provider adapter for Anvia. Source: [`packages/providers/mistral/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/mistral/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/openai.mdx b/apps/docs/content/docs/changelog/openai.mdx index 2b3e719a..22e2c0fc 100644 --- a/apps/docs/content/docs/changelog/openai.mdx +++ b/apps/docs/content/docs/changelog/openai.mdx @@ -9,6 +9,13 @@ OpenAI provider adapter for Anvia. Source: [`packages/providers/openai/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/openai/CHANGELOG.md) +## 0.3.7 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.3.6 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/otel.mdx b/apps/docs/content/docs/changelog/otel.mdx index 90b9f41c..b96975b9 100644 --- a/apps/docs/content/docs/changelog/otel.mdx +++ b/apps/docs/content/docs/changelog/otel.mdx @@ -9,6 +9,13 @@ OpenTelemetry tracing adapter for Anvia. Source: [`packages/observability/otel/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability/otel/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pgvector.mdx b/apps/docs/content/docs/changelog/pgvector.mdx index e0aa61a8..06f2ba6f 100644 --- a/apps/docs/content/docs/changelog/pgvector.mdx +++ b/apps/docs/content/docs/changelog/pgvector.mdx @@ -9,6 +9,13 @@ Postgres pgvector store adapter for Anvia. Source: [`packages/vector-stores/pgvector/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pgvector/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pinecone.mdx b/apps/docs/content/docs/changelog/pinecone.mdx new file mode 100644 index 00000000..80d5f1c5 --- /dev/null +++ b/apps/docs/content/docs/changelog/pinecone.mdx @@ -0,0 +1,17 @@ +--- +title: "@anvia/pinecone" +description: "Release notes for @anvia/pinecone." +--- + +# `@anvia/pinecone` + +Pinecone vector store adapter for Anvia. + +Source: [`packages/vector-stores/pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pinecone/CHANGELOG.md) + +## No release notes yet + +`@anvia/pinecone` is public, but `packages/vector-stores/pinecone/CHANGELOG.md` does not exist yet. + +Release notes will appear here after the next Changesets version bump creates the package changelog. + diff --git a/apps/docs/content/docs/changelog/qdrant.mdx b/apps/docs/content/docs/changelog/qdrant.mdx index 298df780..8cc189dd 100644 --- a/apps/docs/content/docs/changelog/qdrant.mdx +++ b/apps/docs/content/docs/changelog/qdrant.mdx @@ -9,6 +9,13 @@ Qdrant vector store adapter for Anvia. Source: [`packages/vector-stores/qdrant/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/qdrant/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/sandbox.mdx b/apps/docs/content/docs/changelog/sandbox.mdx index 090604c1..a39dbd51 100644 --- a/apps/docs/content/docs/changelog/sandbox.mdx +++ b/apps/docs/content/docs/changelog/sandbox.mdx @@ -9,6 +9,13 @@ Sandboxed workspace tools for Anvia agents. Source: [`packages/tools/sandbox/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/sandbox/CHANGELOG.md) +## 0.2.2 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.1 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/studio.mdx b/apps/docs/content/docs/changelog/studio.mdx index 355f89d5..029b79ee 100644 --- a/apps/docs/content/docs/changelog/studio.mdx +++ b/apps/docs/content/docs/changelog/studio.mdx @@ -9,6 +9,13 @@ Studio UI and HTTP runtime for Anvia agents. Source: [`packages/tools/studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/studio/CHANGELOG.md) +## 0.5.11 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.5.10 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/transformers.mdx b/apps/docs/content/docs/changelog/transformers.mdx index 253f1161..d0579ab2 100644 --- a/apps/docs/content/docs/changelog/transformers.mdx +++ b/apps/docs/content/docs/changelog/transformers.mdx @@ -9,6 +9,13 @@ Transformers.js embedding model adapter for Anvia. Source: [`packages/embeddings/transformers/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/transformers/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/reference/integrations/chroma.mdx b/apps/docs/content/docs/reference/integrations/chroma.mdx index 9d015f71..310cf69e 100644 --- a/apps/docs/content/docs/reference/integrations/chroma.mdx +++ b/apps/docs/content/docs/reference/integrations/chroma.mdx @@ -71,3 +71,14 @@ Purpose: convert Anvia vector filters to Chroma `where` filters. Return behavior: returns `undefined` when no filter is supplied. Notable errors: none directly. + +## ChromaCollectionLike + +```ts +type ChromaCollectionLike = { + upsert(options: Record): Promise; + query(options: Record): Promise; +}; +``` + +Purpose: duck-typed interface for a Chroma collection. diff --git a/apps/docs/content/docs/reference/integrations/milvus.mdx b/apps/docs/content/docs/reference/integrations/milvus.mdx new file mode 100644 index 00000000..ebba33da --- /dev/null +++ b/apps/docs/content/docs/reference/integrations/milvus.mdx @@ -0,0 +1,81 @@ +--- +title: Milvus +description: Public exports from @anvia/milvus. +--- + +Import from `@anvia/milvus`. + +## MilvusVectorStoreConnectOptions + +```ts +type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean; + metric?: "COSINE" | "L2" | "IP"; +}; +``` + +Purpose: connection options for a Milvus collection. + +Return behavior: consumed by `MilvusVectorStore.connect(...)`. + +Notable errors: collection lookup fails when `createIfMissing` is `false` and the collection does not exist. + +Design note: `connect(...)` optionally creates a collection with HNSW index, then loads it into memory. This keeps constructors synchronous and side-effect free while ensuring the collection exists before ingestion or search. + +## MilvusVectorStore + +```ts +class MilvusVectorStore { + static connect( + options: MilvusVectorStoreConnectOptions, + ): Promise>; + upsertDocuments(documents: Array>): Promise; + index(model: EmbeddingModel): MilvusVectorIndex; +} +``` + +Purpose: Milvus-backed document storage. + +Return behavior: `connect(...)` resolves a store; `index(...)` binds it to an embedding model. + +Notable errors: connection and insert calls reject on Milvus errors; `upsertDocuments(...)` throws when a document has no embeddings. + +## MilvusVectorIndex + +```ts +class MilvusVectorIndex + implements VectorSearchIndex { + search(request: VectorSearchRequest): Promise>>; + searchIds(request: VectorSearchRequest): Promise>; + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown>; +} +``` + +Purpose: query-time Milvus search adapter. + +Return behavior: embeds the query, searches Milvus with boolean expression filters, deduplicates multi-embedding document IDs, and returns normalized results. + +Notable errors: embedding or Milvus search failures reject. + +## filterToMilvusExpr + +```ts +function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined; +``` + +Purpose: convert Anvia vector filters to Milvus boolean expression strings. + +Return behavior: returns `undefined` when no filter is supplied. + +Notable errors: none directly. + +## MilvusMetric + +```ts +type MilvusMetric = "COSINE" | "L2" | "IP"; +``` + +Purpose: distance metric for a Milvus collection. diff --git a/apps/docs/content/docs/reference/integrations/pinecone.mdx b/apps/docs/content/docs/reference/integrations/pinecone.mdx new file mode 100644 index 00000000..ba9a0e26 --- /dev/null +++ b/apps/docs/content/docs/reference/integrations/pinecone.mdx @@ -0,0 +1,102 @@ +--- +title: Pinecone +description: Public exports from @anvia/pinecone. +--- + +Import from `@anvia/pinecone`. + +## PineconeVectorStoreConnectOptions + +```ts +type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike; + indexName: string; + namespace?: string; + createIfMissing?: boolean; + metric?: "cosine" | "euclidean" | "dotproduct"; +}; +``` + +Purpose: connection options for a Pinecone index. + +Return behavior: consumed by `PineconeVectorStore.connect(...)`. + +Notable errors: index lookup fails when `createIfMissing` is `false` and the index does not exist. + +Design note: `connect(...)` optionally creates a serverless index, then resolves a namespace handle. This keeps constructors synchronous and side-effect free while ensuring the index exists before ingestion or search. + +## PineconeVectorStore + +```ts +class PineconeVectorStore { + static connect( + options: PineconeVectorStoreConnectOptions, + ): Promise>; + upsertDocuments(documents: Array>): Promise; + index(model: EmbeddingModel): PineconeVectorIndex; +} +``` + +Purpose: Pinecone-backed document storage. + +Return behavior: `connect(...)` resolves a store; `index(...)` binds it to an embedding model. + +Notable errors: connection and upsert calls reject on Pinecone errors; `upsertDocuments(...)` throws when a document has no embeddings. + +## PineconeVectorIndex + +```ts +class PineconeVectorIndex + implements VectorSearchIndex { + search(request: VectorSearchRequest): Promise>>; + searchIds(request: VectorSearchRequest): Promise>; + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown>; +} +``` + +Purpose: query-time Pinecone search adapter. + +Return behavior: embeds the query, queries Pinecone with metadata, deduplicates multi-embedding document IDs, and returns normalized results. + +Notable errors: embedding or Pinecone query failures reject. + +## filterToPineconeFilter + +```ts +function filterToPineconeFilter(filter: VectorFilter | undefined): unknown; +``` + +Purpose: convert Anvia vector filters to Pinecone filter objects. + +Return behavior: returns `undefined` when no filter is supplied. + +Notable errors: none directly. + +## PineconeMetric + +```ts +type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; +``` + +Purpose: distance metric for a Pinecone index. + +## PineconeIndexLike + +```ts +type PineconeIndexLike = { + namespace(namespace: string): PineconeNamespaceLike; +}; +``` + +Purpose: duck-typed interface for a Pinecone index handle. + +## PineconeNamespaceLike + +```ts +type PineconeNamespaceLike = { + upsert(vectors: Array>): Promise; + query(options: Record): Promise; +}; +``` + +Purpose: duck-typed interface for a Pinecone namespace. diff --git a/apps/docs/scripts/check-reference-coverage.mjs b/apps/docs/scripts/check-reference-coverage.mjs index df63c457..b9ea08da 100644 --- a/apps/docs/scripts/check-reference-coverage.mjs +++ b/apps/docs/scripts/check-reference-coverage.mjs @@ -20,6 +20,8 @@ const packageDocs = new Map([ ["@anvia/chroma", "apps/docs/content/docs/reference/integrations/chroma.mdx"], ["@anvia/pgvector", "apps/docs/content/docs/reference/integrations/pgvector.mdx"], ["@anvia/qdrant", "apps/docs/content/docs/reference/integrations/qdrant.mdx"], + ["@anvia/pinecone", "apps/docs/content/docs/reference/integrations/pinecone.mdx"], + ["@anvia/milvus", "apps/docs/content/docs/reference/integrations/milvus.mdx"], ["@anvia/logger", "apps/docs/content/docs/reference/integrations/logger.mdx"], ["@anvia/langfuse", "apps/docs/content/docs/reference/integrations/langfuse.mdx"], ["@anvia/otel", "apps/docs/content/docs/reference/integrations/otel.mdx"], diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts index 9080bbc1..26a371fa 100644 --- a/apps/docs/src/routeTree.gen.ts +++ b/apps/docs/src/routeTree.gen.ts @@ -208,12 +208,3 @@ const rootRouteChildren: RootRouteChildren = { export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() - -import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' -declare module '@tanstack/react-start' { - interface Register { - ssr: true - router: Awaited> - } -} diff --git a/packages/vector-stores/chroma/src/filters.ts b/packages/vector-stores/chroma/src/filters.ts new file mode 100644 index 00000000..94b6c059 --- /dev/null +++ b/packages/vector-stores/chroma/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToChromaWhere(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { [filter.key]: { $eq: filter.value } }; + case "gt": + return { [filter.key]: { $gt: filter.value } }; + case "lt": + return { [filter.key]: { $lt: filter.value } }; + case "and": + return { $and: filter.filters.map(filterToChromaWhere) }; + case "or": + return { $or: filter.filters.map(filterToChromaWhere) }; + } +} diff --git a/packages/vector-stores/chroma/src/helpers.ts b/packages/vector-stores/chroma/src/helpers.ts new file mode 100644 index 00000000..9b77c727 --- /dev/null +++ b/packages/vector-stores/chroma/src/helpers.ts @@ -0,0 +1,103 @@ +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import type { ChromaClientLike, ChromaCollectionLike } from "./types.js"; + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: string | null | undefined): T { + if (document === null || document === undefined) { + return "" as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function chromaRecords( + document: EmbeddedDocument, +): Array<{ + id: string; + document: string; + metadata: VectorMetadata | undefined; + embedding: number[]; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + return document.embeddings.map((embedding, index) => ({ + id: document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`, + document: serializeDocument(document.document), + metadata: document.metadata, + embedding: embedding.vector, + })); +} + +export function logicalDocumentId(id: string): string { + return id.replace(/#embedding:\d+$/, ""); +} + +export function distanceToCosineScore(distance: number): number { + return 1 - distance; +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + ids?: string[][]; + documents?: Array>; + metadatas?: Array>; + distances?: number[][]; + }; + const ids = raw.ids?.[0] ?? []; + const documents = raw.documents?.[0] ?? []; + const metadatas = raw.metadatas?.[0] ?? []; + const distances = raw.distances?.[0] ?? []; + + const results = ids.flatMap((id, index) => { + const score = distanceToCosineScore(distances[index] ?? 0); + if (threshold !== undefined && score < threshold) { + return []; + } + return [ + { + id: logicalDocumentId(id), + score, + document: parseDocument(documents[index]), + ...(metadatas[index] === null || metadatas[index] === undefined + ? {} + : { metadata: metadatas[index] }), + } as VectorSearchResult, + ]; + }); + + const byId = new Map>(); + for (const result of results) { + if (!byId.has(result.id)) { + byId.set(result.id, result); + } + } + return [...byId.values()]; +} + +export async function defaultChromaClient(): Promise { + const chroma = await import("chromadb"); + return new chroma.ChromaClient() as ChromaClientLike; +} + +export async function getOrCreateCollection( + client: ChromaClientLike, + options: Record, +): Promise { + try { + return await client.getCollection(options); + } catch { + return await client.createCollection(options); + } +} diff --git a/packages/vector-stores/chroma/src/index.ts b/packages/vector-stores/chroma/src/index.ts index fe809e34..753a956d 100644 --- a/packages/vector-stores/chroma/src/index.ts +++ b/packages/vector-stores/chroma/src/index.ts @@ -1,220 +1,8 @@ -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -type ChromaClientLike = { - getCollection(options: Record): Promise; - createCollection(options: Record): Promise; - getOrCreateCollection?(options: Record): Promise; -}; - -type ChromaCollectionLike = { - upsert(options: Record): Promise; - query(options: Record): Promise; -}; - -export type ChromaVectorStoreConnectOptions = { - client?: ChromaClientLike | undefined; - collectionName: string; - createIfMissing?: boolean | undefined; - metadata?: Record | undefined; - configuration?: Record | undefined; -}; - -export class ChromaVectorStore { - private constructor(private readonly collection: ChromaCollectionLike) {} - - static async connect( - options: ChromaVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultChromaClient()); - const collectionOptions = { - name: options.collectionName, - metadata: options.metadata ?? { "hnsw:space": "cosine" }, - configuration: options.configuration, - embeddingFunction: null, - }; - const collection = - options.createIfMissing === false - ? await client.getCollection(collectionOptions) - : client.getOrCreateCollection !== undefined - ? await client.getOrCreateCollection(collectionOptions) - : await getOrCreateCollection(client, collectionOptions); - return new ChromaVectorStore(collection); - } - - async upsertDocuments(documents: Array>): Promise { - const records = documents.flatMap((document) => chromaRecords(document)); - await this.collection.upsert({ - ids: records.map((record) => record.id), - documents: records.map((record) => record.document), - embeddings: records.map((record) => record.embedding), - ...(records.some((record) => record.metadata !== undefined) - ? { metadatas: records.map((record) => record.metadata ?? null) } - : {}), - }); - } - - index(model: EmbeddingModel): ChromaVectorIndex { - return new ChromaVectorIndex(model, this.collection); - } -} - -export class ChromaVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly collection: ChromaCollectionLike, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const response = await this.collection.query({ - queryEmbeddings: [queryEmbedding.vector], - nResults: request.topK, - where: filterToChromaWhere(request.filter), - include: ["documents", "metadatas", "distances"], - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToChromaWhere(filter: VectorFilter | undefined): unknown { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": - return { [filter.key]: { $eq: filter.value } }; - case "gt": - return { [filter.key]: { $gt: filter.value } }; - case "lt": - return { [filter.key]: { $lt: filter.value } }; - case "and": - return { $and: filter.filters.map(filterToChromaWhere) }; - case "or": - return { $or: filter.filters.map(filterToChromaWhere) }; - } -} - -async function defaultChromaClient(): Promise { - const chroma = await import("chromadb"); - return new chroma.ChromaClient() as ChromaClientLike; -} - -async function getOrCreateCollection( - client: ChromaClientLike, - options: Record, -): Promise { - try { - return await client.getCollection(options); - } catch { - return await client.createCollection(options); - } -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const raw = response as { - ids?: string[][]; - documents?: Array>; - metadatas?: Array>; - distances?: number[][]; - }; - const ids = raw.ids?.[0] ?? []; - const documents = raw.documents?.[0] ?? []; - const metadatas = raw.metadatas?.[0] ?? []; - const distances = raw.distances?.[0] ?? []; - - const results = ids.flatMap((id, index) => { - const score = distanceToCosineScore(distances[index] ?? 0); - if (threshold !== undefined && score < threshold) { - return []; - } - return [ - { - id: logicalDocumentId(id), - score, - document: parseDocument(documents[index]), - ...(metadatas[index] === null || metadatas[index] === undefined - ? {} - : { metadata: metadatas[index] }), - } as VectorSearchResult, - ]; - }); - - const byId = new Map>(); - for (const result of results) { - if (!byId.has(result.id)) { - byId.set(result.id, result); - } - } - return [...byId.values()]; -} - -function distanceToCosineScore(distance: number): number { - return 1 - distance; -} - -function parseDocument(document: string | null | undefined): T { - if (document === null || document === undefined) { - return "" as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function chromaRecords( - document: EmbeddedDocument, -): Array<{ - id: string; - document: string; - metadata: VectorMetadata | undefined; - embedding: number[]; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - - return document.embeddings.map((embedding, index) => ({ - id: document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`, - document: serializeDocument(document.document), - metadata: document.metadata, - embedding: embedding.vector, - })); -} - -function logicalDocumentId(id: string): string { - return id.replace(/#embedding:\d+$/, ""); -} +export { filterToChromaWhere } from "./filters.js"; +export { ChromaVectorIndex } from "./search-index.js"; +export { ChromaVectorStore } from "./store.js"; +export type { + ChromaClientLike, + ChromaCollectionLike, + ChromaVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/chroma/src/search-index.ts b/packages/vector-stores/chroma/src/search-index.ts new file mode 100644 index 00000000..bc7f72f4 --- /dev/null +++ b/packages/vector-stores/chroma/src/search-index.ts @@ -0,0 +1,40 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToChromaWhere } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { ChromaCollectionLike } from "./types.js"; + +export class ChromaVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly collection: ChromaCollectionLike, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.collection.query({ + queryEmbeddings: [queryEmbedding.vector], + nResults: request.topK, + where: filterToChromaWhere(request.filter), + include: ["documents", "metadatas", "distances"], + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/chroma/src/store.ts b/packages/vector-stores/chroma/src/store.ts new file mode 100644 index 00000000..5dc7be5d --- /dev/null +++ b/packages/vector-stores/chroma/src/store.ts @@ -0,0 +1,43 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { chromaRecords, defaultChromaClient, getOrCreateCollection } from "./helpers.js"; +import { ChromaVectorIndex } from "./search-index.js"; +import type { ChromaCollectionLike, ChromaVectorStoreConnectOptions } from "./types.js"; + +export class ChromaVectorStore { + private constructor(private readonly collection: ChromaCollectionLike) {} + + static async connect( + options: ChromaVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultChromaClient()); + const collectionOptions = { + name: options.collectionName, + metadata: options.metadata ?? { "hnsw:space": "cosine" }, + configuration: options.configuration, + embeddingFunction: null, + }; + const collection = + options.createIfMissing === false + ? await client.getCollection(collectionOptions) + : client.getOrCreateCollection !== undefined + ? await client.getOrCreateCollection(collectionOptions) + : await getOrCreateCollection(client, collectionOptions); + return new ChromaVectorStore(collection); + } + + async upsertDocuments(documents: Array>): Promise { + const records = documents.flatMap((document) => chromaRecords(document)); + await this.collection.upsert({ + ids: records.map((record) => record.id), + documents: records.map((record) => record.document), + embeddings: records.map((record) => record.embedding), + ...(records.some((record) => record.metadata !== undefined) + ? { metadatas: records.map((record) => record.metadata ?? null) } + : {}), + }); + } + + index(model: EmbeddingModel): ChromaVectorIndex { + return new ChromaVectorIndex(model, this.collection); + } +} diff --git a/packages/vector-stores/chroma/src/types.ts b/packages/vector-stores/chroma/src/types.ts new file mode 100644 index 00000000..8a72b79f --- /dev/null +++ b/packages/vector-stores/chroma/src/types.ts @@ -0,0 +1,18 @@ +export type ChromaClientLike = { + getCollection(options: Record): Promise; + createCollection(options: Record): Promise; + getOrCreateCollection?(options: Record): Promise; +}; + +export type ChromaCollectionLike = { + upsert(options: Record): Promise; + query(options: Record): Promise; +}; + +export type ChromaVectorStoreConnectOptions = { + client?: ChromaClientLike | undefined; + collectionName: string; + createIfMissing?: boolean | undefined; + metadata?: Record | undefined; + configuration?: Record | undefined; +}; diff --git a/packages/vector-stores/milvus/package.json b/packages/vector-stores/milvus/package.json new file mode 100644 index 00000000..95204214 --- /dev/null +++ b/packages/vector-stores/milvus/package.json @@ -0,0 +1,43 @@ +{ + "name": "@anvia/milvus", + "version": "0.2.5", + "description": "Milvus vector store adapter for Anvia.", + "author": "anvia", + "maintainer": "Indra Zulfi", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/anvia-hq/anvia", + "directory": "packages/vector-stores/milvus" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@anvia/core": "workspace:*", + "@zilliz/milvus2-sdk-node": "^2.5.0" + }, + "devDependencies": { + "@types/node": "^24.9.1", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vitest": "^4.0.8" + } +} diff --git a/packages/vector-stores/milvus/src/filters.ts b/packages/vector-stores/milvus/src/filters.ts new file mode 100644 index 00000000..8f7b80d7 --- /dev/null +++ b/packages/vector-stores/milvus/src/filters.ts @@ -0,0 +1,44 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": { + const val = milvusLiteral(filter.value); + return `${filter.key} == ${val}`; + } + case "gt": { + const val = milvusLiteral(filter.value); + return `${filter.key} > ${val}`; + } + case "lt": { + const val = milvusLiteral(filter.value); + return `${filter.key} < ${val}`; + } + case "and": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" && ") : undefined; + } + case "or": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" || ") : undefined; + } + } +} + +function milvusLiteral(value: string | number | boolean | null): string { + if (value === null) { + return "null"; + } + if (typeof value === "string") { + return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; + } + return String(value); +} diff --git a/packages/vector-stores/milvus/src/helpers.ts b/packages/vector-stores/milvus/src/helpers.ts new file mode 100644 index 00000000..7e00516f --- /dev/null +++ b/packages/vector-stores/milvus/src/helpers.ts @@ -0,0 +1,152 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentFieldName, + documentIdFieldName, + type MilvusClientLike, + type MilvusMetric, + reservedFieldPrefix, +} from "./types.js"; + +export function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function assertNoReservedFields(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedFieldPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Milvus fields`); + } + } +} + +export function milvusRows( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedFields(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + [documentIdFieldName]: document.id, + [documentFieldName]: serializeDocument(document.document), + vector: embedding.vector, + ...(document.metadata ?? {}), + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + results?: Array< + Array<{ + id: string; + score?: number; + [documentIdFieldName]?: string; + [documentFieldName]?: string; + [key: string]: unknown; + }> + >; + }; + const matches = raw.results?.[0] ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match[documentIdFieldName] ?? match.id); + const result = { + id, + score, + document: parseDocument(match[documentFieldName]), + ...metadataFromRow(match), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultMilvusClient(): Promise { + const { MilvusClient } = await import("@zilliz/milvus2-sdk-node"); + return new MilvusClient({ address: "localhost:19530" }) as unknown as MilvusClientLike; +} + +export async function ensureCollection( + client: MilvusClientLike, + collectionName: string, + vectorSize: number, + metric: MilvusMetric, +): Promise { + const { value: exists } = await client.hasCollection({ collection_name: collectionName }); + if (exists) { + return; + } + + await client.createCollection({ + collection_name: collectionName, + fields: [ + { name: "id", data_type: "VarChar", max_length: 64, is_primary_key: true }, + { name: documentIdFieldName, data_type: "VarChar", max_length: 4096 }, + { name: documentFieldName, data_type: "VarChar", max_length: 65535 }, + { name: "vector", data_type: "FloatVector", dim: vectorSize }, + ], + metric_type: metric, + }); + + await client.createIndex({ + collection_name: collectionName, + field_name: "vector", + index_type: "HNSW", + metric_type: metric, + params: { M: 16, efConstruction: 256 }, + }); +} + +function metadataFromRow( + row: Record, +): { metadata?: Metadata | undefined } { + const skipKeys = new Set(["id", "score", documentIdFieldName, documentFieldName, "vector"]); + const metadata = Object.fromEntries( + Object.entries(row).filter( + ([key]) => !skipKeys.has(key) && !key.startsWith(reservedFieldPrefix), + ), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/milvus/src/index.ts b/packages/vector-stores/milvus/src/index.ts new file mode 100644 index 00000000..1b1b531f --- /dev/null +++ b/packages/vector-stores/milvus/src/index.ts @@ -0,0 +1,8 @@ +export { filterToMilvusExpr } from "./filters.js"; +export { MilvusVectorIndex } from "./search-index.js"; +export { MilvusVectorStore } from "./store.js"; +export type { + MilvusClientLike, + MilvusMetric, + MilvusVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/milvus/src/search-index.ts b/packages/vector-stores/milvus/src/search-index.ts new file mode 100644 index 00000000..c75182bd --- /dev/null +++ b/packages/vector-stores/milvus/src/search-index.ts @@ -0,0 +1,43 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToMilvusExpr } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import { documentFieldName, documentIdFieldName, type MilvusClientLike } from "./types.js"; + +export class MilvusVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const filterExpr = filterToMilvusExpr(request.filter); + const response = await this.client.search({ + collection_name: this.collectionName, + vector: [queryEmbedding.vector], + limit: request.topK, + ...(filterExpr !== undefined ? { filter: filterExpr } : {}), + output_fields: [documentIdFieldName, documentFieldName, "*"], + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/milvus/src/store.ts b/packages/vector-stores/milvus/src/store.ts new file mode 100644 index 00000000..c5f6b32e --- /dev/null +++ b/packages/vector-stores/milvus/src/store.ts @@ -0,0 +1,41 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultMilvusClient, ensureCollection, milvusRows } from "./helpers.js"; +import { MilvusVectorIndex } from "./search-index.js"; +import type { MilvusClientLike, MilvusVectorStoreConnectOptions } from "./types.js"; + +export class MilvusVectorStore { + private constructor( + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + static async connect( + options: MilvusVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultMilvusClient()); + + if (options.createIfMissing !== false) { + await ensureCollection( + client, + options.collectionName, + options.vectorSize, + options.metric ?? "COSINE", + ); + } + + await client.loadCollection({ collection_name: options.collectionName }); + return new MilvusVectorStore(client, options.collectionName); + } + + async upsertDocuments(documents: Array>): Promise { + const rows = documents.flatMap((document) => milvusRows(document)); + await this.client.insert({ + collection_name: this.collectionName, + data: rows, + }); + } + + index(model: EmbeddingModel): MilvusVectorIndex { + return new MilvusVectorIndex(model, this.client, this.collectionName); + } +} diff --git a/packages/vector-stores/milvus/src/types.ts b/packages/vector-stores/milvus/src/types.ts new file mode 100644 index 00000000..dfc925ca --- /dev/null +++ b/packages/vector-stores/milvus/src/types.ts @@ -0,0 +1,22 @@ +export const documentIdFieldName = "__anvia_document_id"; +export const documentFieldName = "__anvia_document"; +export const reservedFieldPrefix = "__anvia_"; + +export type MilvusMetric = "COSINE" | "L2" | "IP"; + +export type MilvusClientLike = { + hasCollection(options: { collection_name: string }): Promise<{ value: boolean }>; + createCollection(options: Record): Promise; + createIndex(options: Record): Promise; + loadCollection(options: { collection_name: string }): Promise; + insert(options: Record): Promise; + search(options: Record): Promise; +}; + +export type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike | undefined; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + metric?: MilvusMetric | undefined; +}; diff --git a/packages/vector-stores/milvus/test/milvus-vector-store.test.ts b/packages/vector-stores/milvus/test/milvus-vector-store.test.ts new file mode 100644 index 00000000..fb0f90b5 --- /dev/null +++ b/packages/vector-stores/milvus/test/milvus-vector-store.test.ts @@ -0,0 +1,196 @@ +import { type Embedding, type EmbeddingModel, embedDocuments } from "@anvia/core/embeddings"; +import { vectorFilter } from "@anvia/core/vector-store"; +import { describe, expect, it } from "vitest"; +import { filterToMilvusExpr, MilvusVectorStore } from "../src/index"; + +class MockEmbeddingModel implements EmbeddingModel { + async embedTexts(texts: string[]): Promise { + return texts.map((document) => ({ + document, + vector: document.toLowerCase().includes("cat") ? [1, 0] : [0, 1], + })); + } +} + +class MockMilvusClient { + readonly collections = new Set(); + readonly creates: unknown[] = []; + readonly indexCreates: unknown[] = []; + readonly inserts: unknown[] = []; + readonly searches: unknown[] = []; + + async hasCollection(options: { collection_name: string }): Promise<{ value: boolean }> { + return { value: this.collections.has(options.collection_name) }; + } + + async createCollection(options: Record): Promise { + this.collections.add(options.collection_name as string); + this.creates.push(options); + return {}; + } + + async createIndex(options: Record): Promise { + this.indexCreates.push(options); + return {}; + } + + async loadCollection(): Promise { + return {}; + } + + async insert(options: Record): Promise { + this.inserts.push(options); + return {}; + } + + async search(options: Record): Promise { + this.searches.push(options); + return { + results: [ + [ + { + id: "point1", + score: 0.9, + __anvia_document_id: "doc1", + __anvia_document: JSON.stringify({ title: "Cat guide" }), + kind: "animal", + }, + { + id: "point2", + score: 0.4, + __anvia_document_id: "doc2", + __anvia_document: "plain dog note", + }, + ], + ], + }; + } +} + +describe("MilvusVectorStore", () => { + it("creates a missing collection with vector size and default COSINE metric", async () => { + const client = new MockMilvusClient(); + + await MilvusVectorStore.connect({ + client, + collectionName: "docs", + vectorSize: 2, + }); + + expect(client.creates[0]).toMatchObject({ + collection_name: "docs", + metric_type: "COSINE", + }); + expect(client.indexCreates[0]).toMatchObject({ + collection_name: "docs", + field_name: "vector", + index_type: "HNSW", + metric_type: "COSINE", + }); + }); + + it("respects createIfMissing false", async () => { + const client = new MockMilvusClient(); + client.collections.add("docs"); + + await MilvusVectorStore.connect({ + client, + collectionName: "docs", + vectorSize: 2, + createIfMissing: false, + }); + + expect(client.creates).toEqual([]); + }); + + it("upserts precomputed embeddings and queries with Anvia embeddings", async () => { + const client = new MockMilvusClient(); + const model = new MockEmbeddingModel(); + const store = await MilvusVectorStore.connect<{ title: string }>({ + client, + collectionName: "docs", + vectorSize: 2, + }); + const embedded = await embedDocuments(model, [{ id: "doc1", title: "Cat guide" }], { + id: (doc) => doc.id, + content: (doc) => doc.title, + metadata: () => ({ kind: "animal" }), + }); + + await store.upsertDocuments(embedded); + const results = await store.index(model).search({ + query: "cat", + topK: 2, + threshold: 0.5, + filter: vectorFilter.eq("kind", "animal"), + }); + + expect(client.inserts[0]).toMatchObject({ + collection_name: "docs", + data: [ + { + vector: [1, 0], + __anvia_document_id: "doc1", + __anvia_document: JSON.stringify({ id: "doc1", title: "Cat guide" }), + kind: "animal", + }, + ], + }); + expect(client.searches[0]).toMatchObject({ + collection_name: "docs", + vector: [[1, 0]], + limit: 2, + filter: 'kind == "animal"', + }); + expect(results).toEqual([ + { + id: "doc1", + score: 0.9, + document: { title: "Cat guide" }, + metadata: { kind: "animal" }, + }, + ]); + }); + + it("translates compound filters", () => { + expect( + filterToMilvusExpr(vectorFilter.and(vectorFilter.gt("rank", 2), vectorFilter.lt("rank", 5))), + ).toEqual("(rank > 2) && (rank < 5)"); + expect( + filterToMilvusExpr(vectorFilter.or(vectorFilter.eq("a", true), vectorFilter.eq("b", false))), + ).toEqual("(a == true) || (b == false)"); + }); + + it("rejects documents with no embeddings", async () => { + const client = new MockMilvusClient(); + const store = await MilvusVectorStore.connect({ + client, + collectionName: "docs", + vectorSize: 2, + }); + + await expect( + store.upsertDocuments([{ id: "doc1", document: "empty", embeddings: [] }]), + ).rejects.toThrow("Document doc1 has no embeddings"); + }); + + it("rejects reserved metadata keys", async () => { + const client = new MockMilvusClient(); + const store = await MilvusVectorStore.connect({ + client, + collectionName: "docs", + vectorSize: 2, + }); + + await expect( + store.upsertDocuments([ + { + id: "doc1", + document: "reserved", + metadata: { __anvia_document_id: "bad" }, + embeddings: [{ document: "reserved", vector: [1, 0] }], + }, + ]), + ).rejects.toThrow("Metadata key __anvia_document_id is reserved"); + }); +}); diff --git a/packages/vector-stores/milvus/tsconfig.json b/packages/vector-stores/milvus/tsconfig.json new file mode 100644 index 00000000..9fd54c2d --- /dev/null +++ b/packages/vector-stores/milvus/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-stores/pgvector/src/filters.ts b/packages/vector-stores/pgvector/src/filters.ts new file mode 100644 index 00000000..d162eb34 --- /dev/null +++ b/packages/vector-stores/pgvector/src/filters.ts @@ -0,0 +1,79 @@ +import type { VectorMetadataValue } from "@anvia/core/embeddings"; +import type { VectorFilter } from "@anvia/core/vector-store"; +import type { PgVectorWhere } from "./types.js"; + +export function filterToPgVectorWhere( + filter: VectorFilter | undefined, + startIndex = 1, +): PgVectorWhere | undefined { + if (filter === undefined) { + return undefined; + } + const state = { nextIndex: startIndex }; + const sql = buildFilterSql(filter, state); + return { sql, values: stateValues(filter) }; +} + +function buildFilterSql(filter: VectorFilter, state: { nextIndex: number }): string { + switch (filter.type) { + case "eq": { + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex}) = $${valueIndex}`; + } + case "gt": { + assertNumericFilterValue(filter.value, filter.type); + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex})::numeric > $${valueIndex}`; + } + case "lt": { + assertNumericFilterValue(filter.value, filter.type); + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex})::numeric < $${valueIndex}`; + } + case "and": + return `(${buildFilterSql(filter.filters[0], state)} AND ${buildFilterSql( + filter.filters[1], + state, + )})`; + case "or": + return `(${buildFilterSql(filter.filters[0], state)} OR ${buildFilterSql( + filter.filters[1], + state, + )})`; + default: { + const _exhaustive: never = filter; + throw new Error(`Unknown filter type: ${(_exhaustive as VectorFilter).type}`); + } + } +} + +function stateValues(filter: VectorFilter): unknown[] { + switch (filter.type) { + case "eq": + return [filter.key, serializeMetadataValue(filter.value)]; + case "gt": + case "lt": + assertNumericFilterValue(filter.value, filter.type); + return [filter.key, filter.value]; + case "and": + case "or": + return [...stateValues(filter.filters[0]), ...stateValues(filter.filters[1])]; + default: { + const _exhaustive: never = filter; + throw new Error(`Unknown filter type: ${(_exhaustive as VectorFilter).type}`); + } + } +} + +function serializeMetadataValue(value: VectorMetadataValue): string | null { + return value === null ? null : String(value); +} + +function assertNumericFilterValue(value: VectorMetadataValue, operator: "gt" | "lt"): void { + if (typeof value !== "number") { + throw new Error(`PgVector ${operator} filters require numeric metadata values`); + } +} diff --git a/packages/vector-stores/pgvector/src/helpers.ts b/packages/vector-stores/pgvector/src/helpers.ts new file mode 100644 index 00000000..dc0abfd9 --- /dev/null +++ b/packages/vector-stores/pgvector/src/helpers.ts @@ -0,0 +1,145 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import type { PgClientLike, PgVectorDistance } from "./types.js"; +import { reservedMetadataPrefix } from "./types.js"; + +export function pointId(id: string): string { + const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( + 16, + 20, + )}-${hex.slice(20)}`; +} + +export function pgVectorRows( + document: EmbeddedDocument, +): Array<{ + id: string; + documentId: string; + document: T; + metadata: Metadata | undefined; + embedding: number[]; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + documentId: document.id, + document: document.document, + metadata: document.metadata, + embedding: embedding.vector, + }; + }); +} + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedMetadataPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia pgvector metadata`); + } + } +} + +export function distanceOperator(distance: PgVectorDistance): "<=>" | "<->" | "<#>" { + switch (distance) { + case "cosine": + return "<=>"; + case "l2": + return "<->"; + case "innerProduct": + return "<#>"; + } +} + +export function scoreFromDistance(distance: number, strategy: PgVectorDistance): number { + return strategy === "cosine" ? 1 - distance : -distance; +} + +export function parseSearchRows( + rows: Array<{ + id: string; + document_id: string; + document: unknown; + metadata: Metadata | null; + distance: number | string; + }>, + threshold: number | undefined, + distanceStrategy: PgVectorDistance, +): Array> { + const byId = new Map>(); + + for (const row of rows) { + const score = scoreFromDistance(Number(row.distance), distanceStrategy); + if (threshold !== undefined && score < threshold) { + continue; + } + const result = { + id: row.document_id, + score, + document: row.document as T, + ...(row.metadata === null ? {} : { metadata: row.metadata }), + } as VectorSearchResult; + const current = byId.get(result.id); + if (current === undefined || result.score > current.score) { + byId.set(result.id, result); + } + } + + return [...byId.values()]; +} + +export function normalizedTopK(topK: number): number { + return Math.max(0, Math.trunc(topK)); +} + +export function quoteQualifiedIdentifier(identifier: string): string { + const parts = identifier.split("."); + if (parts.length === 0 || parts.some((part) => part.length === 0)) { + throw new Error(`Invalid Postgres identifier: ${identifier}`); + } + return parts.map(quoteIdentifier).join("."); +} + +function quoteIdentifier(identifier: string): string { + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { + throw new Error(`Invalid Postgres identifier: ${identifier}`); + } + return `"${identifier.replaceAll('"', '""')}"`; +} + +export async function validateTable( + client: PgClientLike, + tableName: string, + vectorSize: number, +): Promise { + const result = await client.query( + `SELECT a.atttypmod AS vector_size +FROM pg_attribute a +WHERE a.attrelid = $1::regclass + AND a.attname = 'embedding' + AND NOT a.attisdropped`, + [tableName], + ); + const rawSize = result.rows[0]?.vector_size; + if (rawSize === undefined) { + throw new Error(`PgVector table ${tableName} is missing an embedding vector column`); + } + const actualSize = Number(rawSize); + if (actualSize !== vectorSize) { + throw new Error( + `PgVector table ${tableName} has vector size ${actualSize}; expected ${vectorSize}`, + ); + } +} + +export async function defaultPgClient(connectionString: string | undefined): Promise { + const pg = await import("pg"); + return new pg.Pool(connectionString === undefined ? {} : { connectionString }) as PgClientLike; +} diff --git a/packages/vector-stores/pgvector/src/index.ts b/packages/vector-stores/pgvector/src/index.ts index 5f46eb05..4a6afc88 100644 --- a/packages/vector-stores/pgvector/src/index.ts +++ b/packages/vector-stores/pgvector/src/index.ts @@ -1,364 +1,9 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, - type VectorMetadataValue, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; -import pgvector from "pgvector"; - -const reservedMetadataPrefix = "__anvia_"; - -export type PgVectorDistance = "cosine" | "l2" | "innerProduct"; - -export type PgVectorWhere = { - sql: string; - values: unknown[]; -}; - -type PgClientLike = { - query(text: string, values?: readonly unknown[]): Promise<{ rows: Record[] }>; -}; - -export type PgVectorStoreConnectOptions = { - client?: PgClientLike | undefined; - connectionString?: string | undefined; - tableName: string; - vectorSize: number; - createIfMissing?: boolean | undefined; - distance?: PgVectorDistance | undefined; -}; - -export class PgVectorStore { - private constructor( - private readonly client: PgClientLike, - private readonly tableName: string, - private readonly distance: PgVectorDistance, - ) {} - - static async connect( - options: PgVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultPgClient(options.connectionString)); - const tableName = quoteQualifiedIdentifier(options.tableName); - const distance = options.distance ?? "cosine"; - - if (options.createIfMissing !== false) { - await client.query("CREATE EXTENSION IF NOT EXISTS vector"); - await client.query(`CREATE TABLE IF NOT EXISTS ${tableName} ( - id text PRIMARY KEY, - document_id text NOT NULL, - document jsonb NOT NULL, - metadata jsonb, - embedding vector(${options.vectorSize}) NOT NULL -)`); - } - - await validateTable(client, tableName, options.vectorSize); - return new PgVectorStore(client, tableName, distance); - } - - async upsertDocuments(documents: Array>): Promise { - const rows = documents.flatMap((document) => pgVectorRows(document)); - if (rows.length === 0) { - return; - } - - const values = rows.flatMap((row) => [ - row.id, - row.documentId, - JSON.stringify(row.document), - row.metadata === undefined ? null : JSON.stringify(row.metadata), - pgvector.toSql(row.embedding), - ]); - const placeholders = rows.map((_, index) => { - const offset = index * 5; - return `($${offset + 1}, $${offset + 2}, $${offset + 3}::jsonb, $${offset + 4}::jsonb, $${ - offset + 5 - }::vector)`; - }); - - await this.client.query( - `INSERT INTO ${this.tableName} (id, document_id, document, metadata, embedding) -VALUES ${placeholders.join(", ")} -ON CONFLICT (id) DO UPDATE SET - document_id = EXCLUDED.document_id, - document = EXCLUDED.document, - metadata = EXCLUDED.metadata, - embedding = EXCLUDED.embedding`, - values, - ); - } - - index(model: EmbeddingModel): PgVectorIndex { - return new PgVectorIndex(model, this.client, this.tableName, this.distance); - } -} - -export class PgVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly client: PgClientLike, - private readonly tableName: string, - private readonly distance: PgVectorDistance, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const operator = distanceOperator(this.distance); - const where = filterToPgVectorWhere(request.filter, 2); - const limitParameter = 2 + (where?.values.length ?? 0); - const response = await this.client.query( - `SELECT id, document_id, document, metadata, embedding ${operator} $1::vector AS distance -FROM ${this.tableName} -${where === undefined ? "" : `WHERE ${where.sql}`} -ORDER BY embedding ${operator} $1::vector -LIMIT $${limitParameter}`, - [ - pgvector.toSql(queryEmbedding.vector), - ...(where?.values ?? []), - normalizedTopK(request.topK), - ], - ); - - return parseSearchRows( - response.rows as Array<{ - id: string; - document_id: string; - document: unknown; - metadata: Metadata | null; - distance: number | string; - }>, - request.threshold, - this.distance, - ); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToPgVectorWhere( - filter: VectorFilter | undefined, - startIndex = 1, -): PgVectorWhere | undefined { - if (filter === undefined) { - return undefined; - } - const state = { nextIndex: startIndex }; - const sql = buildFilterSql(filter, state); - return { sql, values: stateValues(filter) }; -} - -async function defaultPgClient(connectionString: string | undefined): Promise { - const pg = await import("pg"); - return new pg.Pool(connectionString === undefined ? {} : { connectionString }) as PgClientLike; -} - -async function validateTable( - client: PgClientLike, - tableName: string, - vectorSize: number, -): Promise { - const result = await client.query( - `SELECT a.atttypmod AS vector_size -FROM pg_attribute a -WHERE a.attrelid = $1::regclass - AND a.attname = 'embedding' - AND NOT a.attisdropped`, - [tableName], - ); - const rawSize = result.rows[0]?.vector_size; - if (rawSize === undefined) { - throw new Error(`PgVector table ${tableName} is missing an embedding vector column`); - } - const actualSize = Number(rawSize); - if (actualSize !== vectorSize) { - throw new Error( - `PgVector table ${tableName} has vector size ${actualSize}; expected ${vectorSize}`, - ); - } -} - -function distanceOperator(distance: PgVectorDistance): "<=>" | "<->" | "<#>" { - switch (distance) { - case "cosine": - return "<=>"; - case "l2": - return "<->"; - case "innerProduct": - return "<#>"; - } -} - -function scoreFromDistance(distance: number, strategy: PgVectorDistance): number { - return strategy === "cosine" ? 1 - distance : -distance; -} - -function parseSearchRows( - rows: Array<{ - id: string; - document_id: string; - document: unknown; - metadata: Metadata | null; - distance: number | string; - }>, - threshold: number | undefined, - distanceStrategy: PgVectorDistance, -): Array> { - const byId = new Map>(); - - for (const row of rows) { - const score = scoreFromDistance(Number(row.distance), distanceStrategy); - if (threshold !== undefined && score < threshold) { - continue; - } - const result = { - id: row.document_id, - score, - document: row.document as T, - ...(row.metadata === null ? {} : { metadata: row.metadata }), - } as VectorSearchResult; - const current = byId.get(result.id); - if (current === undefined || result.score > current.score) { - byId.set(result.id, result); - } - } - - return [...byId.values()]; -} - -function pgVectorRows( - document: EmbeddedDocument, -): Array<{ - id: string; - documentId: string; - document: T; - metadata: Metadata | undefined; - embedding: number[]; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - assertNoReservedMetadata(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - documentId: document.id, - document: document.document, - metadata: document.metadata, - embedding: embedding.vector, - }; - }); -} - -function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedMetadataPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia pgvector metadata`); - } - } -} - -function pointId(id: string): string { - const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( - 16, - 20, - )}-${hex.slice(20)}`; -} - -function quoteQualifiedIdentifier(identifier: string): string { - const parts = identifier.split("."); - if (parts.length === 0 || parts.some((part) => part.length === 0)) { - throw new Error(`Invalid Postgres identifier: ${identifier}`); - } - return parts.map(quoteIdentifier).join("."); -} - -function quoteIdentifier(identifier: string): string { - if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { - throw new Error(`Invalid Postgres identifier: ${identifier}`); - } - return `"${identifier.replaceAll('"', '""')}"`; -} - -function normalizedTopK(topK: number): number { - return Math.max(0, Math.trunc(topK)); -} - -function buildFilterSql(filter: VectorFilter, state: { nextIndex: number }): string { - switch (filter.type) { - case "eq": { - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex}) = $${valueIndex}`; - } - case "gt": { - assertNumericFilterValue(filter.value, filter.type); - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex})::numeric > $${valueIndex}`; - } - case "lt": { - assertNumericFilterValue(filter.value, filter.type); - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex})::numeric < $${valueIndex}`; - } - case "and": - return `(${buildFilterSql(filter.filters[0], state)} AND ${buildFilterSql( - filter.filters[1], - state, - )})`; - case "or": - return `(${buildFilterSql(filter.filters[0], state)} OR ${buildFilterSql( - filter.filters[1], - state, - )})`; - } -} - -function stateValues(filter: VectorFilter): unknown[] { - switch (filter.type) { - case "eq": - return [filter.key, serializeMetadataValue(filter.value)]; - case "gt": - case "lt": - assertNumericFilterValue(filter.value, filter.type); - return [filter.key, filter.value]; - case "and": - case "or": - return [...stateValues(filter.filters[0]), ...stateValues(filter.filters[1])]; - } -} - -function serializeMetadataValue(value: VectorMetadataValue): string | null { - return value === null ? null : String(value); -} - -function assertNumericFilterValue(value: VectorMetadataValue, operator: "gt" | "lt"): void { - if (typeof value !== "number") { - throw new Error(`PgVector ${operator} filters require numeric metadata values`); - } -} +export { filterToPgVectorWhere } from "./filters.js"; +export { PgVectorIndex } from "./search-index.js"; +export { PgVectorStore } from "./store.js"; +export type { + PgClientLike, + PgVectorDistance, + PgVectorStoreConnectOptions, + PgVectorWhere, +} from "./types.js"; diff --git a/packages/vector-stores/pgvector/src/search-index.ts b/packages/vector-stores/pgvector/src/search-index.ts new file mode 100644 index 00000000..5d82a809 --- /dev/null +++ b/packages/vector-stores/pgvector/src/search-index.ts @@ -0,0 +1,63 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import pgvector from "pgvector"; +import { filterToPgVectorWhere } from "./filters.js"; +import { distanceOperator, normalizedTopK, parseSearchRows } from "./helpers.js"; +import type { PgClientLike, PgVectorDistance } from "./types.js"; + +export class PgVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: PgClientLike, + private readonly tableName: string, + private readonly distance: PgVectorDistance, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const operator = distanceOperator(this.distance); + const where = filterToPgVectorWhere(request.filter, 2); + const limitParameter = 2 + (where?.values.length ?? 0); + const response = await this.client.query( + `SELECT id, document_id, document, metadata, embedding ${operator} $1::vector AS distance +FROM ${this.tableName} +${where === undefined ? "" : `WHERE ${where.sql}`} +ORDER BY embedding ${operator} $1::vector +LIMIT $${limitParameter}`, + [ + pgvector.toSql(queryEmbedding.vector), + ...(where?.values ?? []), + normalizedTopK(request.topK), + ], + ); + + return parseSearchRows( + response.rows as Array<{ + id: string; + document_id: string; + document: unknown; + metadata: Metadata | null; + distance: number | string; + }>, + request.threshold, + this.distance, + ); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/pgvector/src/store.ts b/packages/vector-stores/pgvector/src/store.ts new file mode 100644 index 00000000..fc9fd40e --- /dev/null +++ b/packages/vector-stores/pgvector/src/store.ts @@ -0,0 +1,76 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import pgvector from "pgvector"; +import { + defaultPgClient, + pgVectorRows, + quoteQualifiedIdentifier, + validateTable, +} from "./helpers.js"; +import { PgVectorIndex } from "./search-index.js"; +import type { PgClientLike, PgVectorDistance, PgVectorStoreConnectOptions } from "./types.js"; + +export class PgVectorStore { + private constructor( + private readonly client: PgClientLike, + private readonly tableName: string, + private readonly distance: PgVectorDistance, + ) {} + + static async connect( + options: PgVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultPgClient(options.connectionString)); + const tableName = quoteQualifiedIdentifier(options.tableName); + const distance = options.distance ?? "cosine"; + + if (options.createIfMissing !== false) { + await client.query("CREATE EXTENSION IF NOT EXISTS vector"); + await client.query(`CREATE TABLE IF NOT EXISTS ${tableName} ( + id text PRIMARY KEY, + document_id text NOT NULL, + document jsonb NOT NULL, + metadata jsonb, + embedding vector(${options.vectorSize}) NOT NULL +)`); + } + + await validateTable(client, tableName, options.vectorSize); + return new PgVectorStore(client, tableName, distance); + } + + async upsertDocuments(documents: Array>): Promise { + const rows = documents.flatMap((document) => pgVectorRows(document)); + if (rows.length === 0) { + return; + } + + const values = rows.flatMap((row) => [ + row.id, + row.documentId, + JSON.stringify(row.document), + row.metadata === undefined ? null : JSON.stringify(row.metadata), + pgvector.toSql(row.embedding), + ]); + const placeholders = rows.map((_, index) => { + const offset = index * 5; + return `($${offset + 1}, $${offset + 2}, $${offset + 3}::jsonb, $${offset + 4}::jsonb, $${ + offset + 5 + }::vector)`; + }); + + await this.client.query( + `INSERT INTO ${this.tableName} (id, document_id, document, metadata, embedding) +VALUES ${placeholders.join(", ")} +ON CONFLICT (id) DO UPDATE SET + document_id = EXCLUDED.document_id, + document = EXCLUDED.document, + metadata = EXCLUDED.metadata, + embedding = EXCLUDED.embedding`, + values, + ); + } + + index(model: EmbeddingModel): PgVectorIndex { + return new PgVectorIndex(model, this.client, this.tableName, this.distance); + } +} diff --git a/packages/vector-stores/pgvector/src/types.ts b/packages/vector-stores/pgvector/src/types.ts new file mode 100644 index 00000000..8389220d --- /dev/null +++ b/packages/vector-stores/pgvector/src/types.ts @@ -0,0 +1,21 @@ +export const reservedMetadataPrefix = "__anvia_"; + +export type PgVectorDistance = "cosine" | "l2" | "innerProduct"; + +export type PgVectorWhere = { + sql: string; + values: unknown[]; +}; + +export type PgClientLike = { + query(text: string, values?: readonly unknown[]): Promise<{ rows: Record[] }>; +}; + +export type PgVectorStoreConnectOptions = { + client?: PgClientLike | undefined; + connectionString?: string | undefined; + tableName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + distance?: PgVectorDistance | undefined; +}; diff --git a/packages/vector-stores/pinecone/package.json b/packages/vector-stores/pinecone/package.json new file mode 100644 index 00000000..3a7f9e2d --- /dev/null +++ b/packages/vector-stores/pinecone/package.json @@ -0,0 +1,43 @@ +{ + "name": "@anvia/pinecone", + "version": "0.2.5", + "description": "Pinecone vector store adapter for Anvia.", + "author": "anvia", + "maintainer": "Indra Zulfi", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/anvia-hq/anvia", + "directory": "packages/vector-stores/pinecone" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@anvia/core": "workspace:*", + "@pinecone-database/pinecone": "^5.1.0" + }, + "devDependencies": { + "@types/node": "^24.9.1", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vitest": "^4.0.8" + } +} diff --git a/packages/vector-stores/pinecone/src/filters.ts b/packages/vector-stores/pinecone/src/filters.ts new file mode 100644 index 00000000..fc226755 --- /dev/null +++ b/packages/vector-stores/pinecone/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToPineconeFilter(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { [filter.key]: { $eq: filter.value } }; + case "gt": + return { [filter.key]: { $gt: filter.value } }; + case "lt": + return { [filter.key]: { $lt: filter.value } }; + case "and": + return { $and: filter.filters.map(filterToPineconeFilter) }; + case "or": + return { $or: filter.filters.map(filterToPineconeFilter) }; + } +} diff --git a/packages/vector-stores/pinecone/src/helpers.ts b/packages/vector-stores/pinecone/src/helpers.ts new file mode 100644 index 00000000..8f8187d0 --- /dev/null +++ b/packages/vector-stores/pinecone/src/helpers.ts @@ -0,0 +1,140 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentIdMetadataKey, + documentMetadataKey, + type PineconeClientLike, + type PineconeMetric, + reservedMetadataPrefix, +} from "./types.js"; + +export function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedMetadataPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Pinecone payloads`); + } + } +} + +export function pineconeVectors( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + values: embedding.vector, + metadata: { + [documentIdMetadataKey]: document.id, + [documentMetadataKey]: serializeDocument(document.document), + ...(document.metadata ?? {}), + }, + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + matches?: Array<{ + id: string; + score?: number; + metadata?: Record | null; + }>; + }; + const matches = raw.matches ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match.metadata?.[documentIdMetadataKey] ?? match.id); + const result = { + id, + score, + document: parseDocument(match.metadata?.[documentMetadataKey]), + ...metadataFromPayload(match.metadata), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultPineconeClient(): Promise { + const { Pinecone } = await import("@pinecone-database/pinecone"); + return new Pinecone() as unknown as PineconeClientLike; +} + +export async function ensureIndex( + client: PineconeClientLike, + indexName: string, + metric: PineconeMetric, +): Promise { + try { + await client.listIndexes(); + } catch { + return; + } + + try { + await client.createIndex({ + name: indexName, + dimension: undefined, + metric, + spec: { serverless: { cloud: "aws", region: "us-east-1" } }, + waitUntilReady: true, + }); + } catch { + // Index already exists + } +} + +function metadataFromPayload( + payload: Record | null | undefined, +): { metadata?: Metadata | undefined } { + const metadata = Object.fromEntries( + Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedMetadataPrefix)), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/pinecone/src/index.ts b/packages/vector-stores/pinecone/src/index.ts new file mode 100644 index 00000000..e50bf67b --- /dev/null +++ b/packages/vector-stores/pinecone/src/index.ts @@ -0,0 +1,10 @@ +export { filterToPineconeFilter } from "./filters.js"; +export { PineconeVectorIndex } from "./search-index.js"; +export { PineconeVectorStore } from "./store.js"; +export type { + PineconeClientLike, + PineconeIndexLike, + PineconeMetric, + PineconeNamespaceLike, + PineconeVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/pinecone/src/search-index.ts b/packages/vector-stores/pinecone/src/search-index.ts new file mode 100644 index 00000000..3c234471 --- /dev/null +++ b/packages/vector-stores/pinecone/src/search-index.ts @@ -0,0 +1,40 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToPineconeFilter } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { PineconeNamespaceLike } from "./types.js"; + +export class PineconeVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly namespace: PineconeNamespaceLike, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.namespace.query({ + vector: queryEmbedding.vector, + topK: request.topK, + filter: filterToPineconeFilter(request.filter), + includeMetadata: true, + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/pinecone/src/store.ts b/packages/vector-stores/pinecone/src/store.ts new file mode 100644 index 00000000..96917a89 --- /dev/null +++ b/packages/vector-stores/pinecone/src/store.ts @@ -0,0 +1,30 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultPineconeClient, ensureIndex, pineconeVectors } from "./helpers.js"; +import { PineconeVectorIndex } from "./search-index.js"; +import type { PineconeNamespaceLike, PineconeVectorStoreConnectOptions } from "./types.js"; + +export class PineconeVectorStore { + private constructor(private readonly namespace: PineconeNamespaceLike) {} + + static async connect( + options: PineconeVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultPineconeClient()); + + if (options.createIfMissing !== false) { + await ensureIndex(client, options.indexName, options.metric ?? "cosine"); + } + + const namespace = client.index(options.indexName).namespace(options.namespace ?? ""); + return new PineconeVectorStore(namespace); + } + + async upsertDocuments(documents: Array>): Promise { + const vectors = documents.flatMap((document) => pineconeVectors(document)); + await this.namespace.upsert(vectors); + } + + index(model: EmbeddingModel): PineconeVectorIndex { + return new PineconeVectorIndex(model, this.namespace); + } +} diff --git a/packages/vector-stores/pinecone/src/types.ts b/packages/vector-stores/pinecone/src/types.ts new file mode 100644 index 00000000..1f6400da --- /dev/null +++ b/packages/vector-stores/pinecone/src/types.ts @@ -0,0 +1,28 @@ +export const documentIdMetadataKey = "__anvia_document_id"; +export const documentMetadataKey = "__anvia_document"; +export const reservedMetadataPrefix = "__anvia_"; + +export type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; + +export type PineconeClientLike = { + listIndexes(): Promise; + createIndex(options: Record): Promise; + index(indexName: string): PineconeIndexLike; +}; + +export type PineconeIndexLike = { + namespace(namespace: string): PineconeNamespaceLike; +}; + +export type PineconeNamespaceLike = { + upsert(vectors: Array>): Promise; + query(options: Record): Promise; +}; + +export type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike | undefined; + indexName: string; + namespace?: string | undefined; + createIfMissing?: boolean | undefined; + metric?: PineconeMetric | undefined; +}; diff --git a/packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts b/packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts new file mode 100644 index 00000000..37a66eb2 --- /dev/null +++ b/packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts @@ -0,0 +1,169 @@ +import { type Embedding, type EmbeddingModel, embedDocuments } from "@anvia/core/embeddings"; +import { vectorFilter } from "@anvia/core/vector-store"; +import { describe, expect, it } from "vitest"; +import { filterToPineconeFilter, PineconeVectorStore } from "../src/index"; + +class MockEmbeddingModel implements EmbeddingModel { + async embedTexts(texts: string[]): Promise { + return texts.map((document) => ({ + document, + vector: document.toLowerCase().includes("cat") ? [1, 0] : [0, 1], + })); + } +} + +class MockNamespace { + readonly upserts: unknown[] = []; + readonly queries: unknown[] = []; + + async upsert(vectors: unknown): Promise { + this.upserts.push(vectors); + } + + async query(options: unknown): Promise { + this.queries.push(options); + return { + matches: [ + { + id: "point1", + score: 0.9, + metadata: { + __anvia_document_id: "doc1", + __anvia_document: JSON.stringify({ title: "Cat guide" }), + kind: "animal", + }, + }, + { + id: "point2", + score: 0.4, + metadata: { + __anvia_document_id: "doc2", + __anvia_document: "plain dog note", + }, + }, + ], + }; + } +} + +class MockPineconeClient { + readonly indexes = new Set(); + readonly creates: unknown[] = []; + readonly namespaces = new Map(); + + async listIndexes(): Promise { + return {}; + } + + async createIndex(options: unknown): Promise { + this.indexes.add((options as { name: string }).name); + this.creates.push(options); + return {}; + } + + index(indexName: string) { + const ns = new MockNamespace(); + this.namespaces.set(indexName, ns); + return { + namespace: () => ns, + }; + } +} + +describe("PineconeVectorStore", () => { + it("connects, upserts precomputed embeddings, and queries with Anvia embeddings", async () => { + const client = new MockPineconeClient(); + const model = new MockEmbeddingModel(); + const store = await PineconeVectorStore.connect<{ title: string }>({ + client, + indexName: "docs", + }); + const ns: MockNamespace = client.namespaces.get("docs") as MockNamespace; + const embedded = await embedDocuments(model, [{ id: "doc1", title: "Cat guide" }], { + id: (doc) => doc.id, + content: (doc) => doc.title, + metadata: () => ({ kind: "animal" }), + }); + + await store.upsertDocuments(embedded); + const results = await store.index(model).search({ + query: "cat", + topK: 2, + threshold: 0.5, + filter: vectorFilter.eq("kind", "animal"), + }); + + expect(ns.upserts[0]).toMatchObject([ + { + values: [1, 0], + metadata: { + __anvia_document_id: "doc1", + __anvia_document: JSON.stringify({ id: "doc1", title: "Cat guide" }), + kind: "animal", + }, + }, + ]); + expect(ns.queries[0]).toMatchObject({ + vector: [1, 0], + topK: 2, + filter: { kind: { $eq: "animal" } }, + includeMetadata: true, + }); + expect(results).toEqual([ + { + id: "doc1", + score: 0.9, + document: { title: "Cat guide" }, + metadata: { kind: "animal" }, + }, + ]); + }); + + it("translates compound filters", () => { + expect( + filterToPineconeFilter( + vectorFilter.and(vectorFilter.gt("rank", 2), vectorFilter.lt("rank", 5)), + ), + ).toEqual({ + $and: [{ rank: { $gt: 2 } }, { rank: { $lt: 5 } }], + }); + expect( + filterToPineconeFilter( + vectorFilter.or(vectorFilter.eq("a", true), vectorFilter.eq("b", false)), + ), + ).toEqual({ + $or: [{ a: { $eq: true } }, { b: { $eq: false } }], + }); + }); + + it("rejects documents with no embeddings", async () => { + const client = new MockPineconeClient(); + const store = await PineconeVectorStore.connect({ + client, + indexName: "docs", + }); + + await expect( + store.upsertDocuments([{ id: "doc1", document: "empty", embeddings: [] }]), + ).rejects.toThrow("Document doc1 has no embeddings"); + }); + + it("rejects reserved metadata keys", async () => { + const client = new MockPineconeClient(); + const store = await PineconeVectorStore.connect({ + client, + indexName: "docs", + }); + + await expect( + store.upsertDocuments([ + { + id: "doc1", + document: "reserved", + metadata: { __anvia_document_id: "bad" }, + embeddings: [{ document: "reserved", vector: [1, 0] }], + }, + ]), + ).rejects.toThrow("Metadata key __anvia_document_id is reserved"); + }); +}); diff --git a/packages/vector-stores/pinecone/tsconfig.json b/packages/vector-stores/pinecone/tsconfig.json new file mode 100644 index 00000000..9fd54c2d --- /dev/null +++ b/packages/vector-stores/pinecone/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-stores/qdrant/src/filters.ts b/packages/vector-stores/qdrant/src/filters.ts new file mode 100644 index 00000000..883755c4 --- /dev/null +++ b/packages/vector-stores/qdrant/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToQdrantFilter(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { must: [{ key: filter.key, match: { value: filter.value } }] }; + case "gt": + return { must: [{ key: filter.key, range: { gt: filter.value } }] }; + case "lt": + return { must: [{ key: filter.key, range: { lt: filter.value } }] }; + case "and": + return { must: filter.filters.map(filterToQdrantFilter) }; + case "or": + return { should: filter.filters.map(filterToQdrantFilter) }; + } +} diff --git a/packages/vector-stores/qdrant/src/helpers.ts b/packages/vector-stores/qdrant/src/helpers.ts new file mode 100644 index 00000000..c28ac3b9 --- /dev/null +++ b/packages/vector-stores/qdrant/src/helpers.ts @@ -0,0 +1,154 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentIdPayloadKey, + documentPayloadKey, + type QdrantClientLike, + reservedPayloadPrefix, +} from "./types.js"; + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedPayloadPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Qdrant payloads`); + } + } +} + +export function pointId(id: string): string { + const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( + 16, + 20, + )}-${hex.slice(20)}`; +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function qdrantPoints( + document: EmbeddedDocument, +): Array<{ + id: string; + vector: number[]; + payload: Record; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + vector: embedding.vector, + payload: { + [documentIdPayloadKey]: document.id, + [documentPayloadKey]: serializeDocument(document.document), + ...(document.metadata ?? {}), + }, + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const points = rawPoints(response); + const byId = new Map>(); + + for (const point of points) { + if (threshold !== undefined && point.score < threshold) { + continue; + } + + const id = String(point.payload?.[documentIdPayloadKey] ?? point.id); + const result = { + id, + score: point.score, + document: parseDocument(point.payload?.[documentPayloadKey]), + ...metadataFromPayload(point.payload), + } as VectorSearchResult; + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultQdrantClient(): Promise { + const qdrant = await import("@qdrant/js-client-rest"); + return new qdrant.QdrantClient({}) as QdrantClientLike; +} + +function rawPoints(response: unknown): Array<{ + id: string | number; + score: number; + payload?: Record | null; +}> { + const raw = response as { + points?: Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>; + result?: + | { + points?: Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>; + } + | Array<{ id: string | number; score?: number; payload?: Record | null }>; + }; + const responseArray = Array.isArray(response) + ? (response as Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>) + : undefined; + const points = + responseArray ?? + (Array.isArray(raw.result) + ? raw.result + : Array.isArray(raw.result?.points) + ? raw.result.points + : raw.points); + return (points ?? []).map((point) => ({ + id: point.id, + score: point.score ?? 0, + ...(point.payload === undefined ? {} : { payload: point.payload }), + })); +} + +function metadataFromPayload( + payload: Record | null | undefined, +): { metadata?: Metadata | undefined } { + const metadata = Object.fromEntries( + Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedPayloadPrefix)), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/qdrant/src/index.ts b/packages/vector-stores/qdrant/src/index.ts index e8a16b35..002af359 100644 --- a/packages/vector-stores/qdrant/src/index.ts +++ b/packages/vector-stores/qdrant/src/index.ts @@ -1,268 +1,8 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -const documentIdPayloadKey = "__anvia_document_id"; -const documentPayloadKey = "__anvia_document"; -const reservedPayloadPrefix = "__anvia_"; - -export type QdrantDistance = "Cosine" | "Dot" | "Euclid"; - -type QdrantClientLike = { - getCollection(collectionName: string): Promise; - createCollection(collectionName: string, options: Record): Promise; - upsert(collectionName: string, options: Record): Promise; - search(collectionName: string, options: Record): Promise; -}; - -export type QdrantVectorStoreConnectOptions = { - client?: QdrantClientLike | undefined; - collectionName: string; - vectorSize: number; - createIfMissing?: boolean | undefined; - distance?: QdrantDistance | undefined; -}; - -export class QdrantVectorStore { - private constructor( - private readonly client: QdrantClientLike, - private readonly collectionName: string, - ) {} - - static async connect( - options: QdrantVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultQdrantClient()); - if (options.createIfMissing === false) { - await client.getCollection(options.collectionName); - return new QdrantVectorStore(client, options.collectionName); - } - - try { - await client.getCollection(options.collectionName); - } catch { - await client.createCollection(options.collectionName, { - vectors: { - size: options.vectorSize, - distance: options.distance ?? "Cosine", - }, - }); - } - return new QdrantVectorStore(client, options.collectionName); - } - - async upsertDocuments(documents: Array>): Promise { - const points = documents.flatMap((document) => qdrantPoints(document)); - await this.client.upsert(this.collectionName, { points }); - } - - index(model: EmbeddingModel): QdrantVectorIndex { - return new QdrantVectorIndex(model, this.client, this.collectionName); - } -} - -export class QdrantVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly client: QdrantClientLike, - private readonly collectionName: string, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const response = await this.client.search(this.collectionName, { - vector: queryEmbedding.vector, - limit: request.topK, - filter: filterToQdrantFilter(request.filter), - with_payload: true, - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToQdrantFilter(filter: VectorFilter | undefined): unknown { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": - return { must: [{ key: filter.key, match: { value: filter.value } }] }; - case "gt": - return { must: [{ key: filter.key, range: { gt: filter.value } }] }; - case "lt": - return { must: [{ key: filter.key, range: { lt: filter.value } }] }; - case "and": - return { must: filter.filters.map(filterToQdrantFilter) }; - case "or": - return { should: filter.filters.map(filterToQdrantFilter) }; - } -} - -async function defaultQdrantClient(): Promise { - const qdrant = await import("@qdrant/js-client-rest"); - return new qdrant.QdrantClient({}) as QdrantClientLike; -} - -function qdrantPoints( - document: EmbeddedDocument, -): Array<{ - id: string; - vector: number[]; - payload: Record; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - assertNoReservedMetadata(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - vector: embedding.vector, - payload: { - [documentIdPayloadKey]: document.id, - [documentPayloadKey]: serializeDocument(document.document), - ...(document.metadata ?? {}), - }, - }; - }); -} - -function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedPayloadPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia Qdrant payloads`); - } - } -} - -function pointId(id: string): string { - const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( - 16, - 20, - )}-${hex.slice(20)}`; -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const points = rawPoints(response); - const byId = new Map>(); - - for (const point of points) { - if (threshold !== undefined && point.score < threshold) { - continue; - } - - const id = String(point.payload?.[documentIdPayloadKey] ?? point.id); - const result = { - id, - score: point.score, - document: parseDocument(point.payload?.[documentPayloadKey]), - ...metadataFromPayload(point.payload), - } as VectorSearchResult; - const current = byId.get(id); - if (current === undefined || result.score > current.score) { - byId.set(id, result); - } - } - - return [...byId.values()]; -} - -function rawPoints(response: unknown): Array<{ - id: string | number; - score: number; - payload?: Record | null; -}> { - const raw = response as { - points?: Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>; - result?: - | { - points?: Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>; - } - | Array<{ id: string | number; score?: number; payload?: Record | null }>; - }; - const responseArray = Array.isArray(response) - ? (response as Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>) - : undefined; - const points = - responseArray ?? - (Array.isArray(raw.result) - ? raw.result - : Array.isArray(raw.result?.points) - ? raw.result.points - : raw.points); - return (points ?? []).map((point) => ({ - id: point.id, - score: point.score ?? 0, - ...(point.payload === undefined ? {} : { payload: point.payload }), - })); -} - -function parseDocument(document: unknown): T { - if (document === null || document === undefined) { - return "" as T; - } - if (typeof document !== "string") { - return document as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function metadataFromPayload( - payload: Record | null | undefined, -): { metadata?: Metadata | undefined } { - const metadata = Object.fromEntries( - Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedPayloadPrefix)), - ) as Metadata; - return Object.keys(metadata).length === 0 ? {} : { metadata }; -} +export { filterToQdrantFilter } from "./filters.js"; +export { QdrantVectorIndex } from "./search-index.js"; +export { QdrantVectorStore } from "./store.js"; +export type { + QdrantClientLike, + QdrantDistance, + QdrantVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/qdrant/src/search-index.ts b/packages/vector-stores/qdrant/src/search-index.ts new file mode 100644 index 00000000..e9bf95ba --- /dev/null +++ b/packages/vector-stores/qdrant/src/search-index.ts @@ -0,0 +1,41 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToQdrantFilter } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { QdrantClientLike } from "./types.js"; + +export class QdrantVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: QdrantClientLike, + private readonly collectionName: string, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.client.search(this.collectionName, { + vector: queryEmbedding.vector, + limit: request.topK, + filter: filterToQdrantFilter(request.filter), + with_payload: true, + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/qdrant/src/store.ts b/packages/vector-stores/qdrant/src/store.ts new file mode 100644 index 00000000..5f25785f --- /dev/null +++ b/packages/vector-stores/qdrant/src/store.ts @@ -0,0 +1,42 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultQdrantClient, qdrantPoints } from "./helpers.js"; +import { QdrantVectorIndex } from "./search-index.js"; +import type { QdrantClientLike, QdrantVectorStoreConnectOptions } from "./types.js"; + +export class QdrantVectorStore { + private constructor( + private readonly client: QdrantClientLike, + private readonly collectionName: string, + ) {} + + static async connect( + options: QdrantVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultQdrantClient()); + if (options.createIfMissing === false) { + await client.getCollection(options.collectionName); + return new QdrantVectorStore(client, options.collectionName); + } + + try { + await client.getCollection(options.collectionName); + } catch { + await client.createCollection(options.collectionName, { + vectors: { + size: options.vectorSize, + distance: options.distance ?? "Cosine", + }, + }); + } + return new QdrantVectorStore(client, options.collectionName); + } + + async upsertDocuments(documents: Array>): Promise { + const points = documents.flatMap((document) => qdrantPoints(document)); + await this.client.upsert(this.collectionName, { points }); + } + + index(model: EmbeddingModel): QdrantVectorIndex { + return new QdrantVectorIndex(model, this.client, this.collectionName); + } +} diff --git a/packages/vector-stores/qdrant/src/types.ts b/packages/vector-stores/qdrant/src/types.ts new file mode 100644 index 00000000..c1aa3ed3 --- /dev/null +++ b/packages/vector-stores/qdrant/src/types.ts @@ -0,0 +1,20 @@ +export const documentIdPayloadKey = "__anvia_document_id"; +export const documentPayloadKey = "__anvia_document"; +export const reservedPayloadPrefix = "__anvia_"; + +export type QdrantDistance = "Cosine" | "Dot" | "Euclid"; + +export type QdrantClientLike = { + getCollection(collectionName: string): Promise; + createCollection(collectionName: string, options: Record): Promise; + upsert(collectionName: string, options: Record): Promise; + search(collectionName: string, options: Record): Promise; +}; + +export type QdrantVectorStoreConnectOptions = { + client?: QdrantClientLike | undefined; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + distance?: QdrantDistance | undefined; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6a55523..1f8df2d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.8 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) apps/docs: dependencies: @@ -497,7 +497,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.8 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) packages/server: devDependencies: @@ -658,6 +658,28 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/milvus: + dependencies: + '@anvia/core': + specifier: workspace:* + version: link:../../core + '@zilliz/milvus2-sdk-node': + specifier: ^2.5.0 + version: 2.6.17 + devDependencies: + '@types/node': + specifier: ^24.9.1 + version: 24.12.2 + tsup: + specifier: ^8.5.0 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.12)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/pgvector: dependencies: '@anvia/core': @@ -686,6 +708,28 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/pinecone: + dependencies: + '@anvia/core': + specifier: workspace:* + version: link:../../core + '@pinecone-database/pinecone': + specifier: ^5.1.0 + version: 5.1.2 + devDependencies: + '@types/node': + specifier: ^24.9.1 + version: 24.12.2 + tsup: + specifier: ^8.5.0 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.12)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/qdrant: dependencies: '@anvia/core': @@ -748,6 +792,109 @@ packages: resolution: {integrity: sha512-IQD9wkVReKAhsEAbDjh/0KrBGTEXelqZLpOBRDaIRvlzZ9sjmUP+gKbpvzyJnei2JHQiE8JAgj7YcNloINbGBw==} engines: {node: '>= 10'} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/checksums@3.1000.6': + resolution: {integrity: sha512-RMCrCteiUwYTEv2G9zfP/BEuKHv57665vVieJyp9cf8VgilWxP/KrWVtMdfdDlIH8nFhvu3rIMc29z3ebGEZ1w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1069.0': + resolution: {integrity: sha512-zkwCW4D88foQ0YHyjVyFhDOIWG+IWiTGXYg8+kUgH6M19son3OsZLvioZGyZdNtlRgLNYfQGBjfArSJOYYt2QQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.974.21': + resolution: {integrity: sha512-P5JAHvn4dTi96UsAGS67LVOqqpUNNRhnfFXqzCYtdBIGZtqBue4CXvRr9YenOO7PALj/Pn8uuyw53FBCiCYw8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.47': + resolution: {integrity: sha512-3YoPwJczcc+MtX2xxXaYaOOWO6xKUJr1ZIIDIFuninr51BYONVVcF/CP8K2xfVRC/PztJjqKWxNGFH7BWQAw1Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.49': + resolution: {integrity: sha512-2UtGUPy+x3lqyceHrtC1uEuVxBZbDalPF6KAFqBwYgm4edWdBrZKNnCqzDs7KynWUvEC6mrR+ojRk+ZgQz9C2w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.54': + resolution: {integrity: sha512-Hx4gO4YRjFwitf3MVl3cDwYe1aryJthC4txVl9b+JAURovA50M2ywf9r8j1E/Q6SCTPT4qQpjOAbKYIC9CG+Vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.53': + resolution: {integrity: sha512-+71sluhkgPqdhbbD3UDwUpj24GCkng9HQx6z7qoBFb8dwkF4ktpOcVKDeHpgg8PvBgLYwAnUYLTEGRC/PniCiQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.56': + resolution: {integrity: sha512-iI+4o0dvQQ4NHel4FMDiFy5q2gaU/ryLK3niOsoPccAt9WLFRkV4XTYPWRr9XvmBUqEzXG73S4p/8gm0Lu/W3A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.47': + resolution: {integrity: sha512-tAizPm9IFo/PHn06c+LQJlzfY2AGOlyF0CUljFejrU6LcZBjnk8pmbZK3/xoIDdnIzjEdbClfvY3mXfr818ZEg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.53': + resolution: {integrity: sha512-pUXE3fu4tfEDV8BksIgf4dXvuIH10FhwHMl/wu8rBD5T1sMpryQWFVitH3kdPS90wlgrGYJQ/meQTSPacyZfeg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.53': + resolution: {integrity: sha512-JmMGlhVvSj8uSG9CpeDkJAXT35H89tc6v84iMgEIE75q4yp1MKVVKvopv6Gg28HJIR7hMNkojRF8H2m5W44wyg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.974.31': + resolution: {integrity: sha512-Yzj6NRYVZdBaCp7o1BwHGyeDBfixdeToLIAMprshIITEdl9wKVSiidVOfeaiH8FyeC1hBmBfDZFvs/aH1Y3xpw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.52': + resolution: {integrity: sha512-rerjP08onRqkBh0AcCqip6GkKvESapmLoTgi1xysZ4C6a1xMrIMtTBcEbUb6EY71oeajnigeUD4KwZjtIO+aWQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.21': + resolution: {integrity: sha512-eC7Vl7Qom/BGhZjG9GEqPwdQ/fk45hg1t5LP4EUxG5d1fdshLbaxCiwh/tszUzDX/4mW40mu2QsbeJJRPBbqUw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.35': + resolution: {integrity: sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1069.0': + resolution: {integrity: sha512-ks4X+kngC3PA5howV7Qu1TgG4bfC4jPykKdvw3nmBSXR9yZxRJouBholFSNQ5kY3L+Fgwyw+LCjzQmNi+KR91g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.13': + resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.8': + resolution: {integrity: sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.30': + resolution: {integrity: sha512-StElZPEoBquWwNqw1AcfpzEyZqJvFxouG+mpDNYlcH6ZOrqd2CuIryv+8LV8gNHZUOyKyJF3Dq9vxaXEmDR9TQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -1092,10 +1239,17 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -1967,6 +2121,9 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@nodable/entities@2.2.0': + resolution: {integrity: sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2330,6 +2487,9 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@petamoriken/float16@3.9.3': + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} + '@phosphor-icons/react@2.1.10': resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} engines: {node: '>=10'} @@ -2337,6 +2497,10 @@ packages: react: '>= 16.8' react-dom: '>= 16.8' + '@pinecone-database/pinecone@5.1.2': + resolution: {integrity: sha512-z7737KUA1hXwd508q1+o4bnRxj0NpMmzA2beyaFm7Y+EC2TYLT5ABuYsn/qhiwiEsYp8v1qS596eBhhvgNagig==} + engines: {node: '>=18.0.0'} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -3322,6 +3486,10 @@ packages: cpu: [x64] os: [win32] + '@shanghaikid/parquetjs@1.8.8': + resolution: {integrity: sha512-uaYXx4yP4cipVU8EZR4OZohiklsazyAcIXtfNEEYBcsMlnz5w4gEvv9vw+cP3yZBv7d1+OQYKfCfKI7oK0iUZQ==} + engines: {node: '>=18.18.2'} + '@shikijs/core@4.0.2': resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} engines: {node: '>=20'} @@ -3361,6 +3529,45 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} + '@smithy/core@3.25.0': + resolution: {integrity: sha512-TTD6el7tvKyafkXBf7XO3jLOE+qVxOTrLjp/fEGiV3BMfUHK/LfdYlQO9YgZvzxC7kqA3H/IhJXNqQgnbgjb7A==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.4.0': + resolution: {integrity: sha512-pPQmNdEvMJttv9z2kdYxoui83p/nr32zjMf0aMfmzmGmFEgKXUfy0vXiNg0fx4R5XLQzmJBLM9Wg0guEq2/q8A==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.5.0': + resolution: {integrity: sha512-OG8kBYAgX7lf32+xLzgirvuLffn1KNoszaSiButt45i2cRa5irk8LQXLYQ5Smij1SBTN4KMNcBsRwRrLPfIGyA==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@4.8.0': + resolution: {integrity: sha512-Mq7TNt/VhlEWiYRLQGpzUWeUxh899UGpjKh7Ru0WVIDIjnE+cTRAn0NYlFQ6bWfsQnKnpCbWJj86HzmcG0qEdg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.5.0': + resolution: {integrity: sha512-vW6UdK7e7gV2wU/tXRsPq4pMQMusb8VymdVOyIFNA1FtyRmEClRFkYDtYI8UcO/HM0wK3qqjvvQs3HOlbgMbdg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.15.0': + resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} @@ -3764,15 +3971,24 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node-int64@0.4.32': + resolution: {integrity: sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@24.12.2': resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + '@types/node@25.9.3': + resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} + '@types/pg@8.20.0': resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==} + '@types/q@1.5.8': + resolution: {integrity: sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3784,6 +4000,12 @@ packages: '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/thrift@0.10.17': + resolution: {integrity: sha512-bDX6d5a5ZDWC81tgDv224n/3PKNYfIQJTPHzlbk4vBWJrYXF6Tg1ncaVmP/c3JbGN2AK9p7zmHorJC2D6oejGQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -3801,6 +4023,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} @@ -3856,6 +4079,9 @@ packages: '@vitest/utils@4.1.5': resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + '@xterm/xterm@5.5.0': + resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} + '@xyflow/react@12.11.0': resolution: {integrity: sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA==} peerDependencies: @@ -3872,6 +4098,18 @@ packages: '@xyflow/system@0.0.77': resolution: {integrity: sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg==} + '@zenfs/core@2.5.6': + resolution: {integrity: sha512-1BLPS6fsBN2keGDErbz548Cu6fT0xbVQlrVYNK5OAr9aviR2mC/F/4wxdb+dQZ4P4pVxxHI2DVouyniZ8yHxTw==} + engines: {node: '>= 18'} + hasBin: true + + '@zilliz/milvus2-sdk-node@2.6.17': + resolution: {integrity: sha512-Z+fCxhuHLV4kogU1T5SS9hDJqLH3KBEceU5snqDV/WDl6uxgNomqZY9md8TTRkHBISqNzDawEaZsi4MF6Y0oWA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3960,6 +4198,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + anynum@1.0.0: + resolution: {integrity: sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3988,6 +4229,12 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -4035,18 +4282,35 @@ packages: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli-wasm@3.0.1: + resolution: {integrity: sha512-U3K72/JAi3jITpdhZBqzSUq+DUY697tLxOuFXB+FpAE/Ug+5C3VZrv4uA674EUZHxNAuQ9wETXNqQkxZD6oL4A==} + engines: {node: '>=v18.0.0'} + + browser-or-node@1.3.0: + resolution: {integrity: sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==} + browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bson@6.10.4: + resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} + engines: {node: '>=16.20.1'} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4237,12 +4501,28 @@ packages: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-convert@3.1.3: + resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} + engines: {node: '>=14.6'} + color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@2.1.0: + resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} + engines: {node: '>=12.20'} + + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + + color@5.0.3: + resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} + engines: {node: '>=18'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -4588,6 +4868,9 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -4720,6 +5003,17 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} engines: {node: '>=18.0.0'} @@ -4764,6 +5058,13 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} + + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} + hasBin: true + fastembed@2.1.0: resolution: {integrity: sha512-oQkpcRHBppJ3+a3w9dU0uytSY0N1cnEa/iVMc8AXEd+tvT529GekOEFhNviJy89R3lvQXF6cdIMTXHj1Gi00xQ==} @@ -4779,6 +5080,9 @@ packages: picomatch: optional: true + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -4801,6 +5105,9 @@ packages: flatbuffers@25.9.23: resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -4962,6 +5269,10 @@ packages: resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -5137,6 +5448,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -5177,6 +5491,9 @@ packages: inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + int53@1.0.0: + resolution: {integrity: sha512-u8BMiMa05OPBgd32CKTead0CVTsFVgwFk23nNXo1teKPF6Sxcu0lXxEzP//zTcaKzXbGgPDXGmj/woyv+I4C5w==} + internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} @@ -5240,6 +5557,10 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -5255,6 +5576,11 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -5331,6 +5657,9 @@ packages: resolution: {integrity: sha512-WHy4Coo+bGZyH7NwJKHkS04YFsFcarWbAEOAC3EMndzdN6VSZqklLLIgfxzyaW9jDoeGYJX9SWbJPKpecox0Uw==} hasBin: true + kerium@1.3.9: + resolution: {integrity: sha512-uoiTcutvUOjOpck8mmUUq7EsuNPhdfFoYMVehLmJGfdLmWC3nABU8KB63MQM0ydkAM4q+2zZmRIKovbx2LF8iA==} + khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} @@ -5338,6 +5667,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -5442,6 +5774,10 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -5451,6 +5787,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@9.1.2: + resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} + engines: {node: 14 || >=16.14} + lucide-react@1.14.0: resolution: {integrity: sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA==} peerDependencies: @@ -5569,6 +5909,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + memium@0.4.5: + resolution: {integrity: sha512-mnmHWWlcyKnkKSSU0qL1Jsg+CsoikVZ2xsOatAN89R+j4V3m3WlQr0nUoqcjKDyUVF5ZGdNinRiZGVd+7A+TJQ==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5795,6 +6138,9 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} @@ -5831,6 +6177,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -5941,6 +6290,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.5.0: + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -6107,6 +6460,10 @@ packages: process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -6130,6 +6487,14 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + qs@6.15.1: resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} @@ -6227,6 +6592,14 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -6467,6 +6840,9 @@ packages: resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} engines: {node: '>=22'} + snappyjs@0.7.0: + resolution: {integrity: sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw==} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -6504,6 +6880,9 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -6529,6 +6908,9 @@ packages: resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -6544,6 +6926,9 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strnum@2.4.0: + resolution: {integrity: sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -6608,6 +6993,9 @@ packages: resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} engines: {node: '>=18'} + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -6619,6 +7007,10 @@ packages: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} + thrift@0.23.0: + resolution: {integrity: sha512-j7F1ls8JogClU88Ta/pwD/OzYuiFeD6Z5GoWw7ip+jcDhcNYFKgfXYEsyLXYpiNfJO94fbLnITGxyfZ19YzA6Q==} + engines: {node: '>= 10.18.0'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -6656,6 +7048,10 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} @@ -6719,6 +7115,9 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} @@ -6808,10 +7207,22 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utilium@3.4.0: + resolution: {integrity: sha512-z4PavqKX0P4XB9SKtXRWWBCDyoDxV6vmTEH4WDBN1/lrYf6MDVk+gntCY6YbryOKjBHGIsYHOS6BTzHf5cF9vQ==} + engines: {node: '>=22.0.0'} + hasBin: true + + uuid@13.0.2: + resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} + hasBin: true + uuid@14.0.0: resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -6954,6 +7365,14 @@ packages: resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} engines: {node: '>=20'} + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.19.0: + resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} + engines: {node: '>= 12.0.0'} + workerd@1.20260430.1: resolution: {integrity: sha512-KEgIWyiw3Jmn+DCd/L3ePo5fmiiYb/UcwKvDWPf/nLLOiwShDFzDSsegU5NY/JcwgvO/QsLHVi2FYrbkcXNY5Q==} engines: {node: '>=16'} @@ -6980,6 +7399,17 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@5.2.5: + resolution: {integrity: sha512-G0gACQIjFmv7NqpaOAXEpe9nEtRYD6ZCy2Ip/B4EzR08qEwnf/wYJoQd3cjosPdnJsHkeh0j2tzOaIBZIOC1yA==} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -7016,6 +7446,10 @@ packages: utf-8-validate: optional: true + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + xmlbuilder2@4.0.3: resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} engines: {node: '>=20.0'} @@ -7024,6 +7458,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -7139,6 +7576,236 @@ snapshots: '@anush008/tokenizers-linux-x64-gnu': 0.0.0 '@anush008/tokenizers-win32-x64-msvc': 0.0.0 + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + '@aws-sdk/util-locate-window': 3.965.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + '@aws-sdk/util-locate-window': 3.965.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/checksums@3.1000.6': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1069.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/credential-provider-node': 3.972.56 + '@aws-sdk/middleware-flexible-checksums': 3.974.31 + '@aws-sdk/middleware-sdk-s3': 3.972.52 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/core@3.974.21': + dependencies: + '@aws-sdk/types': 3.973.13 + '@aws-sdk/xml-builder': 3.972.30 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.25.0 + '@smithy/signature-v4': 5.5.0 + '@smithy/types': 4.15.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.47': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.49': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.54': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/credential-provider-env': 3.972.47 + '@aws-sdk/credential-provider-http': 3.972.49 + '@aws-sdk/credential-provider-login': 3.972.53 + '@aws-sdk/credential-provider-process': 3.972.47 + '@aws-sdk/credential-provider-sso': 3.972.53 + '@aws-sdk/credential-provider-web-identity': 3.972.53 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/credential-provider-imds': 4.4.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.56': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.47 + '@aws-sdk/credential-provider-http': 3.972.49 + '@aws-sdk/credential-provider-ini': 3.972.54 + '@aws-sdk/credential-provider-process': 3.972.47 + '@aws-sdk/credential-provider-sso': 3.972.53 + '@aws-sdk/credential-provider-web-identity': 3.972.53 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/credential-provider-imds': 4.4.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.47': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/token-providers': 3.1069.0 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.974.31': + dependencies: + '@aws-sdk/checksums': 3.1000.6 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.52': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.21': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.35': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/signature-v4': 5.5.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1069.0': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.973.13': + dependencies: + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.8': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.30': + dependencies: + '@smithy/types': 4.15.0 + fast-xml-parser: 5.7.3 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -7532,10 +8199,18 @@ snapshots: '@colors/colors@1.5.0': optional: true + '@colors/colors@1.6.0': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 @@ -7836,7 +8511,7 @@ snapshots: dependencies: lodash.camelcase: 4.3.0 long: 5.3.2 - protobufjs: 7.5.6 + protobufjs: 7.6.1 yargs: 17.7.2 '@hono/node-server@1.19.14(hono@4.12.23)': @@ -8161,6 +8836,8 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nodable/entities@2.2.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8666,11 +9343,15 @@ snapshots: '@oxc-project/types@0.127.0': {} + '@petamoriken/float16@3.9.3': {} + '@phosphor-icons/react@2.1.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + '@pinecone-database/pinecone@5.1.2': {} + '@pinojs/redact@0.4.0': {} '@poppinss/colors@4.1.6': @@ -9500,6 +10181,25 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.2': optional: true + '@shanghaikid/parquetjs@1.8.8': + dependencies: + '@aws-sdk/client-s3': 3.1069.0 + '@types/node-int64': 0.4.32 + '@types/thrift': 0.10.17 + '@zenfs/core': 2.5.6 + brotli-wasm: 3.0.1 + bson: 6.10.4 + int53: 1.0.0 + long: 5.3.2 + node-int64: 0.4.0 + snappyjs: 0.7.0 + thrift: 0.23.0 + varint: 6.0.0 + xxhash-wasm: 1.1.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@shikijs/core@4.0.2': dependencies: '@shikijs/primitive': 4.0.2 @@ -9544,6 +10244,59 @@ snapshots: '@sindresorhus/is@7.2.0': {} + '@smithy/core@3.25.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.4.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.5.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/node-http-handler@4.8.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.5.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/types@4.15.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + '@speed-highlight/core@1.2.15': {} '@stablelib/base64@1.0.1': {} @@ -10012,18 +10765,28 @@ snapshots: '@types/ms@2.1.0': {} + '@types/node-int64@0.4.32': + dependencies: + '@types/node': 24.12.2 + '@types/node@12.20.55': {} '@types/node@24.12.2': dependencies: undici-types: 7.16.0 + '@types/node@25.9.3': + dependencies: + undici-types: 7.24.6 + '@types/pg@8.20.0': dependencies: '@types/node': 24.12.2 pg-protocol: 1.13.0 pg-types: 2.2.0 + '@types/q@1.5.8': {} + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -10034,6 +10797,14 @@ snapshots: '@types/retry@0.12.0': {} + '@types/thrift@0.10.17': + dependencies: + '@types/node': 24.12.2 + '@types/node-int64': 0.4.32 + '@types/q': 1.5.8 + + '@types/triple-beam@1.3.5': {} + '@types/trusted-types@2.0.7': optional: true @@ -10122,6 +10893,9 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@xterm/xterm@5.5.0': + optional: true + '@xyflow/react@12.11.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@xyflow/system': 0.0.77 @@ -10147,6 +10921,36 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 + '@zenfs/core@2.5.6': + dependencies: + '@types/node': 25.9.3 + buffer: 6.0.3 + eventemitter3: 5.0.4 + kerium: 1.3.9 + memium: 0.4.5 + readable-stream: 4.7.0 + utilium: 3.4.0 + + '@zilliz/milvus2-sdk-node@2.6.17': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@grpc/proto-loader': 0.8.0 + '@opentelemetry/api': 1.9.1 + '@petamoriken/float16': 3.9.3 + '@shanghaikid/parquetjs': 1.8.8 + dayjs: 1.11.20 + generic-pool: 3.9.0 + lru-cache: 9.1.2 + protobufjs: 7.6.1 + winston: 3.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -10212,6 +11016,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + anynum@1.0.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -10238,6 +11044,10 @@ snapshots: astring@1.9.0: {} + async-limiter@1.0.1: {} + + async@3.2.6: {} + atomic-sleep@1.0.0: {} auto-bind@5.0.1: {} @@ -10285,10 +11095,16 @@ snapshots: boolean@3.2.0: {} + bowser@2.14.1: {} + braces@3.0.3: dependencies: fill-range: 7.1.1 + brotli-wasm@3.0.1: {} + + browser-or-node@1.3.0: {} + browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.24 @@ -10297,8 +11113,15 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) + bson@6.10.4: {} + buffer-equal-constant-time@1.0.1: {} + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.27.7): dependencies: esbuild: 0.27.7 @@ -10493,10 +11316,25 @@ snapshots: dependencies: color-name: 1.1.4 + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + color-name@1.1.3: {} color-name@1.1.4: {} + color-name@2.1.0: {} + + color-string@2.1.4: + dependencies: + color-name: 2.1.0 + + color@5.0.3: + dependencies: + color-convert: 3.1.3 + color-string: 2.1.4 + comma-separated-tokens@2.0.3: {} commander@4.1.1: {} @@ -10838,6 +11676,8 @@ snapshots: emojilib@2.4.0: {} + enabled@2.0.0: {} + encodeurl@2.0.0: {} encoding-sniffer@0.2.1: @@ -11033,6 +11873,12 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + eventsource-parser@3.0.8: {} eventsource@3.0.7: @@ -11099,6 +11945,18 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-builder@1.2.0: + dependencies: + path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 + + fast-xml-parser@5.7.3: + dependencies: + '@nodable/entities': 2.2.0 + fast-xml-builder: 1.2.0 + path-expression-matcher: 1.5.0 + strnum: 2.4.0 + fastembed@2.1.0: dependencies: '@anush008/tokenizers': 0.0.0 @@ -11115,6 +11973,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fecha@4.2.3: {} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -11148,6 +12008,8 @@ snapshots: flatbuffers@25.9.23: {} + fn.name@1.1.0: {} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -11299,6 +12161,8 @@ snapshots: transitivePeerDependencies: - supports-color + generic-pool@3.9.0: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -11564,6 +12428,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-in-the-middle@3.0.1: @@ -11623,6 +12489,8 @@ snapshots: inline-style-parser@0.2.7: {} + int53@1.0.0: {} + internmap@1.0.1: {} internmap@2.0.3: {} @@ -11666,6 +12534,8 @@ snapshots: is-promise@4.0.0: {} + is-stream@2.0.1: {} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -11676,6 +12546,10 @@ snapshots: isexe@2.0.0: {} + isomorphic-ws@4.0.1(ws@5.2.5): + dependencies: + ws: 5.2.5 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -11746,10 +12620,16 @@ snapshots: dependencies: commander: 8.3.0 + kerium@1.3.9: + dependencies: + utilium: 3.4.0 + khroma@2.1.0: {} kleur@4.1.5: {} + kuler@2.0.0: {} + layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -11819,6 +12699,15 @@ snapshots: lodash.startcase@4.4.0: {} + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + long@5.3.2: {} longest-streak@3.1.0: {} @@ -11827,6 +12716,8 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@9.1.2: {} + lucide-react@1.14.0(react@19.2.5): dependencies: react: 19.2.5 @@ -12051,6 +12942,11 @@ snapshots: media-typer@1.1.0: {} + memium@0.4.5: + dependencies: + kerium: 1.3.9 + utilium: 3.4.0 + merge-descriptors@2.0.0: {} merge2@1.4.1: {} @@ -12444,6 +13340,8 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-int64@0.4.0: {} + node-releases@2.0.38: {} normalize-path@3.0.0: {} @@ -12470,6 +13368,10 @@ snapshots: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -12582,6 +13484,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.5.0: {} + path-key@3.1.1: {} path-to-regexp@6.3.0: {} @@ -12724,6 +13628,8 @@ snapshots: process-warning@5.0.0: {} + process@0.11.10: {} + progress@2.0.3: {} property-information@7.1.0: {} @@ -12778,6 +13684,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + q@1.5.1: {} + qs@6.15.1: dependencies: side-channel: 1.1.0 @@ -12904,6 +13812,20 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdirp@3.6.0: dependencies: picomatch: 2.3.2 @@ -13283,6 +14205,8 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + snappyjs@0.7.0: {} + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -13308,6 +14232,8 @@ snapshots: srvx@0.11.16: {} + stack-trace@0.0.10: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -13334,6 +14260,10 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -13349,6 +14279,10 @@ snapshots: strip-bom@3.0.0: {} + strnum@2.4.0: + dependencies: + anynum: 1.0.0 + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -13415,6 +14349,8 @@ snapshots: terminal-size@4.0.1: {} + text-hex@1.0.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -13427,6 +14363,18 @@ snapshots: dependencies: real-require: 1.0.0 + thrift@0.23.0: + dependencies: + browser-or-node: 1.3.0 + isomorphic-ws: 4.0.1(ws@5.2.5) + node-int64: 0.4.0 + q: 1.5.1 + uuid: 13.0.2 + ws: 5.2.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -13455,6 +14403,8 @@ snapshots: trim-lines@3.0.1: {} + triple-beam@1.4.1: {} + trough@2.2.0: {} ts-algebra@2.0.0: {} @@ -13518,6 +14468,8 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.24.6: {} + undici@6.25.0: {} undici@7.24.8: {} @@ -13628,8 +14580,18 @@ snapshots: util-deprecate@1.0.2: {} + utilium@3.4.0: + dependencies: + eventemitter3: 5.0.4 + optionalDependencies: + '@xterm/xterm': 5.5.0 + + uuid@13.0.2: {} + uuid@14.0.0: {} + varint@6.0.0: {} + vary@1.1.2: {} vfile-location@5.0.3: @@ -13768,6 +14730,26 @@ snapshots: dependencies: string-width: 8.2.1 + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.19.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + workerd@1.20260430.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260430.1 @@ -13806,12 +14788,18 @@ snapshots: wrappy@1.0.2: {} + ws@5.2.5: + dependencies: + async-limiter: 1.0.1 + ws@8.18.0: {} ws@8.20.0: {} ws@8.21.0: {} + xml-naming@0.1.0: {} + xmlbuilder2@4.0.3: dependencies: '@oozcitak/dom': 2.0.2 @@ -13821,6 +14809,8 @@ snapshots: xtend@4.0.2: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} From 7054c3fd23f9ea33bde5fc9ce3ab7662da743686 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:28:36 +0700 Subject: [PATCH 02/16] Version Packages (#103) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/add-pinecone-milvus.md | 6 ------ packages/vector-stores/milvus/CHANGELOG.md | 7 +++++++ packages/vector-stores/milvus/package.json | 2 +- packages/vector-stores/pinecone/CHANGELOG.md | 7 +++++++ packages/vector-stores/pinecone/package.json | 2 +- 5 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 .changeset/add-pinecone-milvus.md create mode 100644 packages/vector-stores/milvus/CHANGELOG.md create mode 100644 packages/vector-stores/pinecone/CHANGELOG.md diff --git a/.changeset/add-pinecone-milvus.md b/.changeset/add-pinecone-milvus.md deleted file mode 100644 index 4e3bb24b..00000000 --- a/.changeset/add-pinecone-milvus.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@anvia/pinecone": minor -"@anvia/milvus": minor ---- - -Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/packages/vector-stores/milvus/CHANGELOG.md b/packages/vector-stores/milvus/CHANGELOG.md new file mode 100644 index 00000000..0a5a84cd --- /dev/null +++ b/packages/vector-stores/milvus/CHANGELOG.md @@ -0,0 +1,7 @@ +# @anvia/milvus + +## 0.3.0 + +### Minor Changes + +- ce25d82: Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/packages/vector-stores/milvus/package.json b/packages/vector-stores/milvus/package.json index 95204214..136b2f60 100644 --- a/packages/vector-stores/milvus/package.json +++ b/packages/vector-stores/milvus/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/milvus", - "version": "0.2.5", + "version": "0.3.0", "description": "Milvus vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-stores/pinecone/CHANGELOG.md b/packages/vector-stores/pinecone/CHANGELOG.md new file mode 100644 index 00000000..0dcdd4f4 --- /dev/null +++ b/packages/vector-stores/pinecone/CHANGELOG.md @@ -0,0 +1,7 @@ +# @anvia/pinecone + +## 0.3.0 + +### Minor Changes + +- ce25d82: Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/packages/vector-stores/pinecone/package.json b/packages/vector-stores/pinecone/package.json index 3a7f9e2d..50a0a83f 100644 --- a/packages/vector-stores/pinecone/package.json +++ b/packages/vector-stores/pinecone/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/pinecone", - "version": "0.2.5", + "version": "0.3.0", "description": "Pinecone vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", From cf68bda22c30ae8dde78eb6ac515f2f1969ee8b4 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:11:00 +0700 Subject: [PATCH 03/16] docs: add pinecone and milvus package pages (#104) --- apps/docs/content/docs/changelog/index.mdx | 8 +- apps/docs/content/docs/changelog/milvus.mdx | 6 +- apps/docs/content/docs/changelog/pinecone.mdx | 6 +- apps/docs/content/docs/changelog/react.mdx | 6 + apps/docs/content/docs/changelog/studio.mdx | 7 + apps/docs/content/docs/packages/index.mdx | 2 + apps/docs/content/docs/packages/meta.json | 2 + apps/docs/content/docs/packages/milvus.mdx | 176 ++++++++++++++++++ apps/docs/content/docs/packages/pinecone.mdx | 172 +++++++++++++++++ apps/docs/src/routes/index.tsx | 6 +- 10 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 apps/docs/content/docs/packages/milvus.mdx create mode 100644 apps/docs/content/docs/packages/pinecone.mdx diff --git a/apps/docs/content/docs/changelog/index.mdx b/apps/docs/content/docs/changelog/index.mdx index 870ac269..efe513bf 100644 --- a/apps/docs/content/docs/changelog/index.mdx +++ b/apps/docs/content/docs/changelog/index.mdx @@ -36,9 +36,9 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | | `@anvia/chroma` | `0.2.5` | [View changelog](/docs/changelog/chroma) | -| `@anvia/milvus` | `0.2.5` | [View changelog](/docs/changelog/milvus) | +| `@anvia/milvus` | `0.3.0` | [View changelog](/docs/changelog/milvus) | | `@anvia/pgvector` | `0.2.5` | [View changelog](/docs/changelog/pgvector) | -| `@anvia/pinecone` | `0.2.5` | [View changelog](/docs/changelog/pinecone) | +| `@anvia/pinecone` | `0.3.0` | [View changelog](/docs/changelog/pinecone) | | `@anvia/qdrant` | `0.2.5` | [View changelog](/docs/changelog/qdrant) | ## Logger @@ -59,13 +59,13 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | | `@anvia/sandbox` | `0.2.2` | [View changelog](/docs/changelog/sandbox) | -| `@anvia/studio` | `0.5.11` | [View changelog](/docs/changelog/studio) | +| `@anvia/studio` | `0.5.12` | [View changelog](/docs/changelog/studio) | ## React | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/react` | `0.3.1` | [View changelog](/docs/changelog/react) | +| `@anvia/react` | `0.4.0` | [View changelog](/docs/changelog/react) | ## Server diff --git a/apps/docs/content/docs/changelog/milvus.mdx b/apps/docs/content/docs/changelog/milvus.mdx index 48c82f41..a66574b6 100644 --- a/apps/docs/content/docs/changelog/milvus.mdx +++ b/apps/docs/content/docs/changelog/milvus.mdx @@ -9,9 +9,9 @@ Milvus vector store adapter for Anvia. Source: [`packages/vector-stores/milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/milvus/CHANGELOG.md) -## No release notes yet +## 0.3.0 -`@anvia/milvus` is public, but `packages/vector-stores/milvus/CHANGELOG.md` does not exist yet. +### Minor Changes -Release notes will appear here after the next Changesets version bump creates the package changelog. +- ce25d82: Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/apps/docs/content/docs/changelog/pinecone.mdx b/apps/docs/content/docs/changelog/pinecone.mdx index 80d5f1c5..717f6e91 100644 --- a/apps/docs/content/docs/changelog/pinecone.mdx +++ b/apps/docs/content/docs/changelog/pinecone.mdx @@ -9,9 +9,9 @@ Pinecone vector store adapter for Anvia. Source: [`packages/vector-stores/pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pinecone/CHANGELOG.md) -## No release notes yet +## 0.3.0 -`@anvia/pinecone` is public, but `packages/vector-stores/pinecone/CHANGELOG.md` does not exist yet. +### Minor Changes -Release notes will appear here after the next Changesets version bump creates the package changelog. +- ce25d82: Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/apps/docs/content/docs/changelog/react.mdx b/apps/docs/content/docs/changelog/react.mdx index 7af9d142..f384f54b 100644 --- a/apps/docs/content/docs/changelog/react.mdx +++ b/apps/docs/content/docs/changelog/react.mdx @@ -9,6 +9,12 @@ React hooks and client transports for Anvia applications. Source: [`packages/react/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/react/CHANGELOG.md) +## 0.4.0 + +### Minor Changes + +- da736e9: Add `useCompletion` hook for single-prompt text completion streaming and `createDirectTransport` for in-process transport without HTTP. + ## 0.3.1 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/studio.mdx b/apps/docs/content/docs/changelog/studio.mdx index 029b79ee..a5efc0b3 100644 --- a/apps/docs/content/docs/changelog/studio.mdx +++ b/apps/docs/content/docs/changelog/studio.mdx @@ -9,6 +9,13 @@ Studio UI and HTTP runtime for Anvia agents. Source: [`packages/tools/studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/studio/CHANGELOG.md) +## 0.5.12 + +### Patch Changes + +- Updated dependencies [da736e9] + - @anvia/react@0.4.0 + ## 0.5.11 ### Patch Changes diff --git a/apps/docs/content/docs/packages/index.mdx b/apps/docs/content/docs/packages/index.mdx index cbd1bf55..5e246db9 100644 --- a/apps/docs/content/docs/packages/index.mdx +++ b/apps/docs/content/docs/packages/index.mdx @@ -35,6 +35,8 @@ Each guide covers one `@anvia/*` package end to end: installation, configuration | Package | Purpose | | --- | --- | | [`@anvia/chroma`](/docs/packages/chroma) | ChromaDB vector store adapter | +| [`@anvia/milvus`](/docs/packages/milvus) | Milvus vector store adapter | +| [`@anvia/pinecone`](/docs/packages/pinecone) | Pinecone vector store adapter | | [`@anvia/pgvector`](/docs/packages/pgvector) | Postgres pgvector vector store adapter | | [`@anvia/qdrant`](/docs/packages/qdrant) | Qdrant vector store adapter | diff --git a/apps/docs/content/docs/packages/meta.json b/apps/docs/content/docs/packages/meta.json index 5a235807..7cffcdbb 100644 --- a/apps/docs/content/docs/packages/meta.json +++ b/apps/docs/content/docs/packages/meta.json @@ -20,6 +20,8 @@ "transformers", "---Vector Stores---", "chroma", + "milvus", + "pinecone", "pgvector", "qdrant", "---Observability---", diff --git a/apps/docs/content/docs/packages/milvus.mdx b/apps/docs/content/docs/packages/milvus.mdx new file mode 100644 index 00000000..77235f13 --- /dev/null +++ b/apps/docs/content/docs/packages/milvus.mdx @@ -0,0 +1,176 @@ +--- +title: "@anvia/milvus" +description: Milvus vector store adapter for Anvia. +--- + +`@anvia/milvus` connects Anvia's vector store interface to Milvus. Use it to store embedded documents in a Milvus collection and search them through Anvia retrieval workflows. + +## Install + +```sh +pnpm add @anvia/milvus +``` + +The Milvus client (`@zilliz/milvus2-sdk-node`) is a transitive dependency. By default, the adapter connects to `localhost:19530`; pass a custom client when you need a different endpoint. + +## Quick Start + +```ts +import { createFastEmbedEmbeddingModel } from "@anvia/fastembed"; +import { MilvusVectorStore } from "@anvia/milvus"; + +const model = await createFastEmbedEmbeddingModel(); + +const store = await MilvusVectorStore.connect({ + collectionName: "documents", + vectorSize: 384, // must match your embedding model's dimensions +}); + +await store.upsertDocuments([ + { + id: "doc-1", + document: "Anvia is a TypeScript AI agent framework.", + embeddings: await model.embedTexts(["Anvia is a TypeScript AI agent framework."]), + }, +]); + +const index = store.index(model); +const results = await index.search({ query: "What is Anvia?", topK: 5 }); +console.log(results[0].document); +``` + +## Connection Options + +```ts +type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike; // pre-configured Milvus client + collectionName: string; // required + vectorSize: number; // required: embedding dimensions + createIfMissing?: boolean; // default: true + metric?: MilvusMetric; // default: "COSINE" +}; +``` + +```ts +// Default: creates the collection if missing and loads it +const store = await MilvusVectorStore.connect({ + collectionName: "docs", + vectorSize: 384, +}); + +// Don't create if missing +const store = await MilvusVectorStore.connect({ + collectionName: "docs", + vectorSize: 384, + createIfMissing: false, +}); + +// Custom distance metric +const store = await MilvusVectorStore.connect({ + collectionName: "docs", + vectorSize: 384, + metric: "IP", +}); +``` + +When `createIfMissing` is `true` (default), the adapter: + +1. Checks whether the collection exists +2. Creates a collection with an `id`, document fields, and a `FloatVector` field +3. Creates an HNSW index on the vector field +4. Loads the collection before returning the store + +## Upserting Documents + +```ts +await store.upsertDocuments([ + { + id: "doc-1", + document: { title: "Getting Started", content: "..." }, + metadata: { category: "guide", version: 2 }, + embeddings: await model.embedTexts(["Getting Started content..."]), + }, +]); +``` + +- Documents are inserted as Milvus rows with document content and metadata fields +- Documents with multiple embeddings get indexed as `doc-1#embedding:0`, `doc-1#embedding:1`, etc. +- Row IDs are deterministic SHA-256 hashes of the document ID + +## Searching + +```ts +const index = store.index(model); + +// Basic search +const results = await index.search({ query: "agent tools", topK: 5 }); + +// With threshold +const results = await index.search({ + query: "agent tools", + topK: 10, + threshold: 0.7, +}); + +// With metadata filter +const results = await index.search({ + query: "agent tools", + topK: 5, + filter: { type: "eq", key: "category", value: "guide" }, +}); +``` + +## Metadata Filters + +```ts +// Equality +{ type: "eq", key: "category", value: "guide" } + +// Range +{ type: "gt", key: "version", value: 2 } +{ type: "lt", key: "version", value: 5 } + +// Logical combinators +{ type: "and", filters: [...] } +{ type: "or", filters: [...] } +``` + +Filters are translated to Milvus boolean expressions such as `category == "guide"` or `(category == "guide") && (version > 2)`. + +## Distance Metrics + +| Metric | Milvus value | Notes | +| --- | --- | --- | +| Cosine (default) | `"COSINE"` | Best for text embeddings | +| L2 | `"L2"` | Euclidean distance | +| Inner product | `"IP"` | Dot product similarity | + +## Agent Tool + +Expose the vector index as an agent tool: + +```ts +import { AgentBuilder } from "@anvia/core"; + +const searchTool = index.asTool({ + name: "search_docs", + description: "Search the documentation.", +}); + +const agent = new AgentBuilder("support", model) + .tool(searchTool) + .build(); +``` + +## Error Handling + +- `connect()` throws if Milvus is unreachable +- `connect({ createIfMissing: false })` throws if the collection does not exist +- `upsertDocuments()` throws if a document has zero embeddings +- Metadata keys starting with `__anvia_` are reserved and rejected + +## Related + +- [Retrieval Guide](/docs/guides/retrieval) for RAG concepts +- [`@anvia/fastembed`](/docs/packages/fastembed) for local embeddings +- [`@anvia/transformers`](/docs/packages/transformers) for local Transformers.js embeddings diff --git a/apps/docs/content/docs/packages/pinecone.mdx b/apps/docs/content/docs/packages/pinecone.mdx new file mode 100644 index 00000000..fc3971e2 --- /dev/null +++ b/apps/docs/content/docs/packages/pinecone.mdx @@ -0,0 +1,172 @@ +--- +title: "@anvia/pinecone" +description: Pinecone vector store adapter for Anvia. +--- + +`@anvia/pinecone` connects Anvia's vector store interface to Pinecone. Use it to store embedded documents in a Pinecone index and query them through Anvia retrieval workflows. + +## Install + +```sh +pnpm add @anvia/pinecone +``` + +The Pinecone client (`@pinecone-database/pinecone`) is a transitive dependency. Configure Pinecone credentials the same way you would for the official client. + +## Quick Start + +```ts +import { createFastEmbedEmbeddingModel } from "@anvia/fastembed"; +import { PineconeVectorStore } from "@anvia/pinecone"; + +const model = await createFastEmbedEmbeddingModel(); + +const store = await PineconeVectorStore.connect({ + indexName: "documents", + namespace: "support", +}); + +await store.upsertDocuments([ + { + id: "doc-1", + document: "Anvia is a TypeScript AI agent framework.", + embeddings: await model.embedTexts(["Anvia is a TypeScript AI agent framework."]), + }, +]); + +const index = store.index(model); +const results = await index.search({ query: "What is Anvia?", topK: 5 }); +console.log(results[0].document); +``` + +## Connection Options + +```ts +type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike; // pre-configured Pinecone client + indexName: string; // required + namespace?: string; // default: "" + createIfMissing?: boolean; // default: true + metric?: PineconeMetric; // default: "cosine" +}; +``` + +```ts +// Default: creates the index if missing and uses the default namespace +const store = await PineconeVectorStore.connect({ + indexName: "docs", +}); + +// Use a namespace +const store = await PineconeVectorStore.connect({ + indexName: "docs", + namespace: "production", +}); + +// Don't create if missing +const store = await PineconeVectorStore.connect({ + indexName: "docs", + createIfMissing: false, +}); + +// Custom distance metric +const store = await PineconeVectorStore.connect({ + indexName: "docs", + metric: "dotproduct", +}); +``` + +## Upserting Documents + +```ts +await store.upsertDocuments([ + { + id: "doc-1", + document: { title: "Getting Started", content: "..." }, + metadata: { category: "guide", version: 2 }, + embeddings: await model.embedTexts(["Getting Started content..."]), + }, +]); +``` + +- Documents are stored as Pinecone vectors with document content and metadata in the vector metadata +- Documents with multiple embeddings get indexed as `doc-1#embedding:0`, `doc-1#embedding:1`, etc. +- Vector IDs are deterministic SHA-256 hashes of the document ID + +## Searching + +```ts +const index = store.index(model); + +// Basic search +const results = await index.search({ query: "agent tools", topK: 5 }); + +// With threshold +const results = await index.search({ + query: "agent tools", + topK: 10, + threshold: 0.7, +}); + +// With metadata filter +const results = await index.search({ + query: "agent tools", + topK: 5, + filter: { type: "eq", key: "category", value: "guide" }, +}); +``` + +## Metadata Filters + +```ts +// Equality +{ type: "eq", key: "category", value: "guide" } + +// Range +{ type: "gt", key: "version", value: 2 } +{ type: "lt", key: "version", value: 5 } + +// Logical combinators +{ type: "and", filters: [...] } +{ type: "or", filters: [...] } +``` + +Filters are translated to Pinecone's native filter format (`$eq`, `$gt`, `$lt`, `$and`, `$or`). + +## Distance Metrics + +| Metric | Pinecone value | Notes | +| --- | --- | --- | +| Cosine (default) | `"cosine"` | Best for text embeddings | +| Euclidean | `"euclidean"` | L2 distance | +| Dot product | `"dotproduct"` | Dot product similarity | + +## Agent Tool + +Expose the vector index as an agent tool: + +```ts +import { AgentBuilder } from "@anvia/core"; + +const searchTool = index.asTool({ + name: "search_docs", + description: "Search the documentation.", +}); + +const agent = new AgentBuilder("support", model) + .tool(searchTool) + .build(); +``` + +## Error Handling + +- `connect()` throws if Pinecone credentials or network access fail +- `connect({ createIfMissing: false })` throws if the index does not exist +- `upsertDocuments()` throws if a document has zero embeddings +- Metadata keys starting with `__anvia_` are reserved and rejected + +## Related + +- [Retrieval Guide](/docs/guides/retrieval) for RAG concepts +- [`@anvia/fastembed`](/docs/packages/fastembed) for local embeddings +- [`@anvia/transformers`](/docs/packages/transformers) for local Transformers.js embeddings diff --git a/apps/docs/src/routes/index.tsx b/apps/docs/src/routes/index.tsx index 199a4506..37c5dc3c 100644 --- a/apps/docs/src/routes/index.tsx +++ b/apps/docs/src/routes/index.tsx @@ -35,9 +35,9 @@ export const Route = createFileRoute("/")({ }); const metrics = [ - { value: "17", label: "Runtime packages", icon: Package }, + { value: "19", label: "Runtime packages", icon: Package }, { value: "4", label: "Model adapters", icon: Boxes }, - { value: "8", label: "Integration packages", icon: BookOpen }, + { value: "10", label: "Integration packages", icon: BookOpen }, { value: "1", label: "Core package", icon: Zap }, ]; @@ -118,6 +118,8 @@ const packageGroups = [ "@anvia/fastembed", "@anvia/transformers", "@anvia/chroma", + "@anvia/milvus", + "@anvia/pinecone", "@anvia/qdrant", "@anvia/pgvector", ], From 18403b7efdc7aba8c7553f6608d65377d38054b0 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:11:44 +0700 Subject: [PATCH 04/16] feat: add sandbox v2 controls (#105) --- .changeset/warm-sandboxes-stream.md | 5 + .../docs/guides/sandbox/agent-tools.mdx | 36 +- .../docs/guides/sandbox/docker-sessions.mdx | 82 ++- .../content/docs/guides/sandbox/index.mdx | 4 +- apps/docs/content/docs/packages/sandbox.mdx | 47 +- .../content/docs/reference/api-coverage.mdx | 2 +- apps/docs/content/docs/reference/sandbox.mdx | 96 +++- packages/tools/sandbox/src/docker-sandbox.ts | 474 ++++++++++++++---- packages/tools/sandbox/src/errors.ts | 4 + packages/tools/sandbox/src/tools.ts | 63 ++- packages/tools/sandbox/src/types.ts | 88 +++- .../test/docker-sandbox.integration.test.ts | 102 ++++ packages/tools/sandbox/test/tools.test.ts | 59 +++ 13 files changed, 930 insertions(+), 132 deletions(-) create mode 100644 .changeset/warm-sandboxes-stream.md diff --git a/.changeset/warm-sandboxes-stream.md b/.changeset/warm-sandboxes-stream.md new file mode 100644 index 00000000..ea721fea --- /dev/null +++ b/.changeset/warm-sandboxes-stream.md @@ -0,0 +1,5 @@ +--- +"@anvia/sandbox": minor +--- + +Add sandbox V2 controls for persistent workspaces, lifecycle cleanup, streaming command execution, file-size limits, observability hooks, language presets, and model-facing tool policies. diff --git a/apps/docs/content/docs/guides/sandbox/agent-tools.mdx b/apps/docs/content/docs/guides/sandbox/agent-tools.mdx index 4c23a608..ae484e3c 100644 --- a/apps/docs/content/docs/guides/sandbox/agent-tools.mdx +++ b/apps/docs/content/docs/guides/sandbox/agent-tools.mdx @@ -12,9 +12,24 @@ import { createSandboxTools, DockerSandbox } from "@anvia/sandbox"; const sandbox = new DockerSandbox(); const session = await sandbox.createSession(); +const tools = createSandboxTools(session, { + allow: ["exec_command", "read_file", "write_file", "list_files"], + exec: { + allowedCommands: ["node", "npm"], + defaultTimeoutMs: 10_000, + maxTimeoutMs: 30_000, + }, + readFile: { + maxBytes: 64_000, + }, + writeFile: { + maxBytes: 64_000, + }, +}); + const agent = new AgentBuilder("coder", model) .instructions("Use the sandbox for code execution and file operations.") - .tools(createSandboxTools(session)) + .tools(tools) .defaultMaxTurns(6) .build(); @@ -39,3 +54,22 @@ The default bundle exposes: | `list_files` | List files and directories under a workspace path | Keep the session lifetime explicit. Create the session before the agent run and destroy it in a `finally` block. + +## Tool Policy + +Use `allow` to choose which sandbox tools are exposed. Use `exec.allowedCommands` or `exec.blockedCommands` to keep model-chosen commands inside the product boundary. + +```ts +const tools = createSandboxTools(session, { + allow: ["exec_command", "read_file", "list_files"], + exec: { + allowedCommands: ["node", "npm"], + maxTimeoutMs: 30_000, + }, + readFile: { + maxBytes: 64_000, + }, +}); +``` + +Tool policy is separate from Docker isolation. Docker limits protect the host; tool policy controls what the model can ask the session to do. diff --git a/apps/docs/content/docs/guides/sandbox/docker-sessions.mdx b/apps/docs/content/docs/guides/sandbox/docker-sessions.mdx index 0fc34101..3dd81ed5 100644 --- a/apps/docs/content/docs/guides/sandbox/docker-sessions.mdx +++ b/apps/docs/content/docs/guides/sandbox/docker-sessions.mdx @@ -1,6 +1,6 @@ --- title: Docker Sessions -description: Create and clean up ephemeral Docker sandbox sessions. +description: Create, stream, persist, and clean up Docker sandbox sessions. --- Create a `DockerSandbox`, start a session, run commands, then destroy the session when the workflow is done. @@ -10,12 +10,23 @@ import { DockerSandbox } from "@anvia/sandbox"; const sandbox = new DockerSandbox({ image: "node:22-bookworm", + workspace: { + mode: "ephemeral", + }, + lifecycle: { + ttlMs: 10 * 60_000, + idleTimeoutMs: 60_000, + }, limits: { timeoutMs: 30_000, maxOutputBytes: 64_000, + maxFileBytes: 5_000_000, memoryMb: 512, cpus: 1, }, + network: { + mode: "none", + }, }); const session = await sandbox.createSession({ @@ -43,3 +54,72 @@ try { `DockerSandbox` disables network access by default, drops Docker capabilities, enables `no-new-privileges`, rejects unsafe paths, truncates large output, and enforces command timeouts. Do not bind mount the host project into a sandbox for untrusted code. Stage the files a task needs through `manifest`, `writeFile(...)`, or `write_file`. + +## Workspace Modes + +The default workspace mode is ephemeral. Each session gets a new Docker volume, and `destroy()` removes it. + +```ts +const sandbox = new DockerSandbox({ + workspace: { + mode: "ephemeral", + }, +}); +``` + +Use a persistent workspace only when continuity is intentional. Persistent workspaces reuse a named Docker volume and keep it after `destroy()` by default. + +```ts +const sandbox = new DockerSandbox({ + workspace: { + mode: "persistent", + id: `user-${userId}`, + }, +}); +``` + +You can also choose persistence per session: + +```ts +const session = await sandbox.createSession({ + workspace: { + mode: "persistent", + id: `session-${sessionId}`, + }, +}); +``` + +## Streaming Commands + +Use `execStream(...)` when command output should appear while the command is still running. + +```ts +for await (const event of session.execStream({ + command: "npm", + args: ["test"], + timeoutMs: 60_000, +})) { + if (event.type === "stdout" || event.type === "stderr") { + process.stdout.write(event.text); + } + + if (event.type === "exit") { + console.log(event.result.exitCode); + } +} +``` + +## Lifecycle Cleanup + +Set lifecycle timers when a session should clean itself up even if application code forgets to call `destroy()`. + +```ts +const sandbox = new DockerSandbox({ + lifecycle: { + ttlMs: 10 * 60_000, + idleTimeoutMs: 60_000, + }, +}); +``` + +`ttlMs` is a maximum lifetime. `idleTimeoutMs` starts after the session has no active operations. diff --git a/apps/docs/content/docs/guides/sandbox/index.mdx b/apps/docs/content/docs/guides/sandbox/index.mdx index 923c03c9..2c710e9d 100644 --- a/apps/docs/content/docs/guides/sandbox/index.mdx +++ b/apps/docs/content/docs/guides/sandbox/index.mdx @@ -1,6 +1,6 @@ --- title: Sandbox -description: Run code and file operations inside an ephemeral Docker workspace. +description: Run code and file operations inside an isolated Docker workspace. --- `@anvia/sandbox` lets an agent work inside an isolated Docker workspace instead of the host filesystem. @@ -26,6 +26,6 @@ Use a sandbox when an agent needs a scratch workspace for operations such as: | Inspect command output | Capture stdout, stderr, exit code, and timeout state | | Run multi-step tool flows | Share files between sandbox tools without exposing project files | -V1 sessions are ephemeral. `destroy()` removes the container and workspace volume. +Sessions are ephemeral by default. `destroy()` removes the container and workspace volume unless you explicitly choose a persistent workspace. Next, create your first [Docker session](/docs/guides/sandbox/docker-sessions). diff --git a/apps/docs/content/docs/packages/sandbox.mdx b/apps/docs/content/docs/packages/sandbox.mdx index b019995e..9e2f753d 100644 --- a/apps/docs/content/docs/packages/sandbox.mdx +++ b/apps/docs/content/docs/packages/sandbox.mdx @@ -36,12 +36,15 @@ type DockerSandboxOptions = { image?: string; // Docker image (default: "node:22-bookworm") pull?: "missing" | "always" | "never"; // pull policy (default: "missing") workdir?: string; // workspace directory (default: "/workspace") - network?: boolean | "none" | "host" | string; // network mode (default: false/none) + workspace?: SandboxWorkspaceOptions; // ephemeral or persistent volume + lifecycle?: SandboxLifecycleOptions; // ttl and idle cleanup + network?: boolean | "none" | "host" | string | { mode: boolean | "none" | "host" | string }; user?: string; // container user dockerPath?: string; // path to docker CLI labels?: Record; // Docker container labels limits?: SandboxLimits; // resource limits security?: DockerSandboxSecurityOptions; + hooks?: SandboxHooks; }; ``` @@ -49,15 +52,23 @@ type DockerSandboxOptions = { // Custom image and limits const sandbox = new DockerSandbox({ image: "python:3.12-slim", - limits: { timeoutMs: 30_000, memoryMb: 512, cpus: 1 }, + limits: { timeoutMs: 30_000, maxFileBytes: 5_000_000, memoryMb: 512, cpus: 1 }, }); // Network access enabled const sandbox = new DockerSandbox({ - network: "host", + network: { mode: "host" }, }); ``` +### Presets + +```ts +const nodeSandbox = DockerSandbox.node(); +const pythonSandbox = DockerSandbox.python(); +const denoSandbox = DockerSandbox.deno(); +``` + ### Security Defaults By default, sandboxes run with: @@ -86,6 +97,7 @@ Each session creates one Docker container and one Docker volume mounted at the w ```ts const session = await sandbox.createSession({ id: "my-session", // optional custom ID + workspace: { mode: "ephemeral" }, metadata: { userId: "123" }, // optional metadata manifest: { files: { @@ -100,6 +112,17 @@ const session = await sandbox.createSession({ The manifest seeds the workspace with files and directories before any commands run. +Use a persistent workspace only when continuity is explicit: + +```ts +const session = await sandbox.createSession({ + workspace: { + mode: "persistent", + id: `user-${userId}`, + }, +}); +``` + ### Executing Commands ```ts @@ -122,6 +145,16 @@ console.log(result.durationMs); console.log(result.timedOut); ``` +Stream command output with `execStream(...)`: + +```ts +for await (const event of session.execStream({ command: "npm", args: ["test"] })) { + if (event.type === "stdout" || event.type === "stderr") { + process.stdout.write(event.text); + } +} +``` + ### File Operations ```ts @@ -155,7 +188,13 @@ import { createSandboxTools } from "@anvia/sandbox"; import { AgentBuilder } from "@anvia/core"; const session = await sandbox.createSession(); -const tools = createSandboxTools(session); +const tools = createSandboxTools(session, { + allow: ["exec_command", "read_file", "write_file", "list_files"], + exec: { + allowedCommands: ["node", "npm"], + maxTimeoutMs: 30_000, + }, +}); const agent = new AgentBuilder("coder", model) .instructions("Write and run code to solve problems.") diff --git a/apps/docs/content/docs/reference/api-coverage.mdx b/apps/docs/content/docs/reference/api-coverage.mdx index 146ad728..4a709c45 100644 --- a/apps/docs/content/docs/reference/api-coverage.mdx +++ b/apps/docs/content/docs/reference/api-coverage.mdx @@ -16,7 +16,7 @@ Internal source-file exports are outside this check unless they are re-exported | Package | Public entrypoints | Public exports | Reference coverage | | --- | ---: | ---: | --- | | `@anvia/core` | 19 | 250 | [Core](/docs/reference/core) | -| `@anvia/sandbox` | 1 | 23 | [Sandbox](/docs/reference/sandbox) | +| `@anvia/sandbox` | 1 | 36 | [Sandbox](/docs/reference/sandbox) | | `@anvia/openai` | 1 | 17 | [OpenAI Provider](/docs/reference/providers/openai) | | `@anvia/gemini` | 1 | 13 | [Gemini Provider](/docs/reference/providers/gemini) | | `@anvia/anthropic` | 1 | 4 | [Anthropic Provider](/docs/reference/providers/anthropic) | diff --git a/apps/docs/content/docs/reference/sandbox.mdx b/apps/docs/content/docs/reference/sandbox.mdx index 0f3b721a..919bc63f 100644 --- a/apps/docs/content/docs/reference/sandbox.mdx +++ b/apps/docs/content/docs/reference/sandbox.mdx @@ -11,6 +11,9 @@ Import from `@anvia/sandbox`. class DockerSandbox implements Sandbox { readonly provider: "docker"; constructor(options?: DockerSandboxOptions); + static node(options?: DockerSandboxOptions): DockerSandbox; + static python(options?: DockerSandboxOptions): DockerSandbox; + static deno(options?: DockerSandboxOptions): DockerSandbox; createSession(options?: SandboxCreateSessionOptions): Promise; } ``` @@ -34,6 +37,7 @@ interface SandboxSession { readonly provider: string; readonly workdir: string; exec(options: SandboxExecOptions): Promise; + execStream(options: SandboxExecOptions): AsyncIterable; readFile(path: string): Promise; readTextFile(path: string): Promise; writeFile(path: string, data: string | Uint8Array): Promise; @@ -52,10 +56,19 @@ Return behavior: file paths are sandbox-relative; absolute paths and traversal o ```ts type SandboxCreateSessionOptions = { id?: string; + workspace?: SandboxWorkspaceOptions; manifest?: SandboxManifest; metadata?: Record; }; +type SandboxWorkspaceOptions = + | { mode?: "ephemeral" } + | { + mode: "persistent"; + id: string; + destroyOnSessionDestroy?: boolean; + }; + type SandboxManifest = { files?: Record; directories?: string[]; @@ -65,13 +78,20 @@ type SandboxManifest = { type SandboxLimits = { timeoutMs?: number; maxOutputBytes?: number; + maxFileBytes?: number; memoryMb?: number; cpus?: number; pidsLimit?: number; }; + +type SandboxLifecycleOptions = { + ttlMs?: number; + idleTimeoutMs?: number; + autoDestroy?: boolean; +}; ``` -Purpose: seed files, directories, environment, metadata, and runtime limits for a sandbox session. +Purpose: seed files, directories, environment, metadata, workspace mode, lifecycle cleanup, and runtime limits for a sandbox session. ## Docker Options @@ -80,12 +100,15 @@ type DockerSandboxOptions = { image?: string; pull?: "missing" | "always" | "never"; workdir?: string; - network?: SandboxNetworkMode; + workspace?: SandboxWorkspaceOptions; + lifecycle?: SandboxLifecycleOptions; + network?: SandboxNetworkMode | DockerSandboxNetworkOptions; user?: string; dockerPath?: string; labels?: Record; limits?: SandboxLimits; security?: DockerSandboxSecurityOptions; + hooks?: SandboxHooks; }; type DockerSandboxSecurityOptions = { @@ -94,11 +117,17 @@ type DockerSandboxSecurityOptions = { dropCapabilities?: string[]; }; +type DockerSandboxNetworkOptions = { + mode: SandboxNetworkMode; +}; + type SandboxNetworkMode = boolean | "none" | "host" | string; ``` Defaults: `image` is `node:22-bookworm`, `pull` is `missing`, `workdir` is `/workspace`, network access is disabled, `noNewPrivileges` is enabled, and Docker capabilities are dropped with `["ALL"]`. +Static presets: `DockerSandbox.node(...)`, `DockerSandbox.python(...)`, and `DockerSandbox.deno(...)` create sandboxes with language-specific default images. + ## Execution ```ts @@ -124,9 +153,14 @@ type SandboxExecResult = { stdoutTruncated: boolean; stderrTruncated: boolean; }; + +type SandboxExecStreamEvent = + | { type: "stdout"; chunk: Uint8Array; text: string } + | { type: "stderr"; chunk: Uint8Array; text: string } + | { type: "exit"; result: SandboxExecResult }; ``` -Return behavior: non-zero command exits are returned in `SandboxExecResult`; infrastructure failures throw. +Return behavior: non-zero command exits are returned in `SandboxExecResult`; infrastructure failures throw. `execStream(...)` yields stdout and stderr chunks as they arrive, then yields one `exit` event with the final result. ## Files @@ -151,8 +185,12 @@ function createSandboxTools( ): AnyTool[]; type SandboxToolsOptions = { + allow?: SandboxToolName[]; include?: SandboxToolName[]; execTimeoutMs?: number; + exec?: SandboxExecToolPolicy; + readFile?: SandboxFileToolPolicy; + writeFile?: SandboxFileToolPolicy; }; type SandboxToolName = @@ -161,13 +199,59 @@ type SandboxToolName = | "write_file" | "list_files"; +type SandboxExecToolPolicy = { + allowedCommands?: string[]; + blockedCommands?: string[]; + defaultTimeoutMs?: number; + maxTimeoutMs?: number; +}; + +type SandboxFileToolPolicy = { + maxBytes?: number; +}; + type SandboxToolsFactory = ( session: SandboxSession, options?: SandboxToolsOptions, ) => AnyTool[]; ``` -Purpose: expose a live sandbox session as Anvia tools. The default bundle includes `exec_command`, `read_file`, `write_file`, and `list_files`. +Purpose: expose a live sandbox session as Anvia tools. The default bundle includes `exec_command`, `read_file`, `write_file`, and `list_files`. Use `allow` or `include` to select tools and `SandboxExecToolPolicy` or `SandboxFileToolPolicy` to bound model-facing operations. + +## Hooks + +```ts +type SandboxHooks = { + onSessionCreate?: (event: SandboxSessionEvent) => void | Promise; + onExecStart?: (event: SandboxExecEvent) => void | Promise; + onExecEnd?: (event: SandboxExecEndEvent) => void | Promise; + onFileWrite?: (event: SandboxFileWriteEvent) => void | Promise; + onDestroy?: (event: SandboxSessionEvent) => void | Promise; +}; + +type SandboxSessionEvent = { + sessionId: string; + provider: string; + workdir: string; +}; + +type SandboxExecEvent = SandboxSessionEvent & { + command: string; + args: string[]; + cwd?: string; +}; + +type SandboxExecEndEvent = SandboxExecEvent & { + result: SandboxExecResult; +}; + +type SandboxFileWriteEvent = SandboxSessionEvent & { + path: string; + size: number; +}; +``` + +Purpose: observe session creation, command execution, file writes, and cleanup without exposing those details to the model. For workflow guidance, see [Sandbox](/docs/guides/sandbox) and the `tools:11` cookbook example. @@ -180,6 +264,8 @@ class SandboxDockerCommandError extends SandboxError {} class SandboxSessionDestroyedError extends SandboxError {} class SandboxPathError extends SandboxError {} class SandboxTimeoutError extends SandboxError {} +class SandboxFileSizeError extends SandboxError {} +class SandboxToolPolicyError extends SandboxError {} ``` -Purpose: typed failures for Docker setup, path validation, destroyed sessions, and sandbox operations. +Purpose: typed failures for Docker setup, path validation, destroyed sessions, file size limits, tool policy limits, and sandbox operations. diff --git a/packages/tools/sandbox/src/docker-sandbox.ts b/packages/tools/sandbox/src/docker-sandbox.ts index 4c4b7a0c..06a49776 100644 --- a/packages/tools/sandbox/src/docker-sandbox.ts +++ b/packages/tools/sandbox/src/docker-sandbox.ts @@ -5,6 +5,7 @@ import path from "node:path"; import { assertDockerCli, runDockerCli } from "./docker-cli"; import { SandboxDockerCommandError, + SandboxFileSizeError, SandboxSessionDestroyedError, SandboxTimeoutError, } from "./errors"; @@ -15,12 +16,16 @@ import type { SandboxCreateSessionOptions, SandboxExecOptions, SandboxExecResult, + SandboxExecStreamEvent, SandboxFileEntry, SandboxFileType, + SandboxHooks, + SandboxLifecycleOptions, SandboxLimits, SandboxManifest, - SandboxNetworkMode, SandboxSession, + SandboxSessionEvent, + SandboxWorkspaceOptions, } from "./types"; const defaultImage = "node:22-bookworm"; @@ -34,17 +39,29 @@ export class DockerSandbox implements Sandbox { private readonly image: string; private readonly pull: "missing" | "always" | "never"; private readonly workdir: string; - private readonly network: SandboxNetworkMode; + private readonly workspace: SandboxWorkspaceOptions; + private readonly lifecycle: Required> & + Omit; + private readonly network: NonNullable; private readonly dockerPath: string; private readonly labels: Record; private readonly limits: SandboxLimits; private readonly security: Required>; + private readonly hooks: SandboxHooks; private readonly user: string | undefined; constructor(options: DockerSandboxOptions = {}) { this.image = options.image ?? defaultImage; this.pull = options.pull ?? "missing"; this.workdir = options.workdir ?? defaultWorkdir; + this.workspace = options.workspace ?? { mode: "ephemeral" }; + this.lifecycle = { + autoDestroy: options.lifecycle?.autoDestroy ?? true, + ...(options.lifecycle?.ttlMs === undefined ? {} : { ttlMs: options.lifecycle.ttlMs }), + ...(options.lifecycle?.idleTimeoutMs === undefined + ? {} + : { idleTimeoutMs: options.lifecycle.idleTimeoutMs }), + }; this.network = options.network ?? false; this.dockerPath = options.dockerPath ?? "docker"; this.labels = options.labels ?? {}; @@ -54,23 +71,42 @@ export class DockerSandbox implements Sandbox { noNewPrivileges: options.security?.noNewPrivileges ?? true, dropCapabilities: options.security?.dropCapabilities ?? ["ALL"], }; + this.hooks = options.hooks ?? {}; this.user = options.user; } + static node(options: DockerSandboxOptions = {}): DockerSandbox { + return new DockerSandbox({ ...options, image: options.image ?? "node:22-bookworm" }); + } + + static python(options: DockerSandboxOptions = {}): DockerSandbox { + return new DockerSandbox({ ...options, image: options.image ?? "python:3.13-bookworm" }); + } + + static deno(options: DockerSandboxOptions = {}): DockerSandbox { + return new DockerSandbox({ ...options, image: options.image ?? "denoland/deno:debian" }); + } + async createSession(options: SandboxCreateSessionOptions = {}): Promise { await this.ensureImage(); const id = sanitizeResourceId(options.id ?? randomUUID()); + const workspace = options.workspace ?? this.workspace; + const workspaceId = getWorkspaceId(workspace, id); const containerName = `anvia-sandbox-${id}`; - const volumeName = `anvia-sandbox-${id}-workspace`; + const volumeName = `anvia-sandbox-${workspaceId}-workspace`; + const removeVolumeOnDestroy = shouldDestroyWorkspace(workspace); await assertDockerCli(["volume", "create", volumeName], this.cliOptions()); try { - await assertDockerCli(this.createRunArgs(containerName, volumeName, options.metadata), { - ...this.cliOptions(), - timeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs, - }); + await assertDockerCli( + this.createRunArgs(containerName, volumeName, workspace, options.metadata), + { + ...this.cliOptions(), + timeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs, + }, + ); const session = new DockerSandboxSession({ id, @@ -79,13 +115,17 @@ export class DockerSandbox implements Sandbox { workdir: this.workdir, dockerPath: this.dockerPath, limits: this.limits, + lifecycle: this.lifecycle, + removeVolumeOnDestroy, env: options.manifest?.env ?? {}, + hooks: this.hooks, }); await session.applyManifest(options.manifest); + await this.hooks.onSessionCreate?.(session.event()); return session; } catch (error) { - await this.cleanup(containerName, volumeName); + await this.cleanup(containerName, removeVolumeOnDestroy ? volumeName : undefined); throw error; } } @@ -107,6 +147,7 @@ export class DockerSandbox implements Sandbox { private createRunArgs( containerName: string, volumeName: string, + workspace: SandboxWorkspaceOptions, metadata: Record | undefined, ): string[] { const args = [ @@ -120,6 +161,10 @@ export class DockerSandbox implements Sandbox { this.workdir, "--label", "anvia.sandbox=true", + "--label", + `anvia.sandbox.workspace.mode=${workspace.mode ?? "ephemeral"}`, + "--label", + `anvia.sandbox.workspace.volume=${volumeName}`, ]; for (const [key, value] of Object.entries(this.labels)) { @@ -150,13 +195,15 @@ export class DockerSandbox implements Sandbox { } private appendNetworkArgs(args: string[]): void { - if (this.network === false || this.network === "none") { + const mode = typeof this.network === "object" ? this.network.mode : this.network; + + if (mode === false || mode === "none") { args.push("--network", "none"); return; } - if (this.network !== true) { - args.push("--network", this.network); + if (mode !== true) { + args.push("--network", mode); } } @@ -195,11 +242,14 @@ export class DockerSandbox implements Sandbox { }; } - private async cleanup(containerName: string, volumeName: string): Promise { + private async cleanup(containerName: string, volumeName: string | undefined): Promise { await runDockerCli(["rm", "-f", containerName], this.cliOptions()).catch(() => undefined); - await runDockerCli(["volume", "rm", "-f", volumeName], this.cliOptions()).catch( - () => undefined, - ); + + if (volumeName !== undefined) { + await runDockerCli(["volume", "rm", "-f", volumeName], this.cliOptions()).catch( + () => undefined, + ); + } } } @@ -212,7 +262,14 @@ class DockerSandboxSession implements SandboxSession { private readonly volumeName: string; private readonly dockerPath: string; private readonly limits: SandboxLimits; + private readonly lifecycle: Required> & + Omit; + private readonly removeVolumeOnDestroy: boolean; private readonly env: Record; + private readonly hooks: SandboxHooks; + private ttlTimer: ReturnType | undefined; + private idleTimer: ReturnType | undefined; + private activeOperations = 0; private destroyed = false; constructor(options: { @@ -222,7 +279,11 @@ class DockerSandboxSession implements SandboxSession { workdir: string; dockerPath: string; limits: SandboxLimits; + lifecycle: Required> & + Omit; + removeVolumeOnDestroy: boolean; env: Record; + hooks: SandboxHooks; }) { this.id = options.id; this.containerName = options.containerName; @@ -230,109 +291,154 @@ class DockerSandboxSession implements SandboxSession { this.workdir = options.workdir; this.dockerPath = options.dockerPath; this.limits = options.limits; + this.lifecycle = options.lifecycle; + this.removeVolumeOnDestroy = options.removeVolumeOnDestroy; this.env = options.env; + this.hooks = options.hooks; + this.startLifecycleTimers(); } async applyManifest(manifest: SandboxManifest | undefined): Promise { - this.assertActive(); - - for (const directory of manifest?.directories ?? []) { - await this.mkdir(directory); - } + await this.runOperation(async () => { + for (const directory of manifest?.directories ?? []) { + await this.mkdir(directory); + } - for (const [filePath, content] of Object.entries(manifest?.files ?? {})) { - await this.writeFile(filePath, content); - } + for (const [filePath, content] of Object.entries(manifest?.files ?? {})) { + await this.writeFile(filePath, content); + } + }); } async exec(options: SandboxExecOptions): Promise { - this.assertActive(); - - if (options.command.trim().length === 0) { - throw new SandboxDockerCommandError("Sandbox command cannot be empty.", { - stdout: "", - stderr: "", - exitCode: 1, + return this.runOperation(async () => { + await this.hooks.onExecStart?.({ + ...this.event(), + command: options.command, + args: options.args ?? [], + ...(options.cwd === undefined ? {} : { cwd: options.cwd }), }); - } - - const args = ["exec"]; - if (options.input !== undefined) { - args.push("-i"); - } + const normalizedResult = await this.execCommand(options); - const cwd = containerPath(this.workdir, options.cwd ?? "."); - args.push("-w", cwd); + await this.hooks.onExecEnd?.({ + ...this.event(), + command: options.command, + args: options.args ?? [], + ...(options.cwd === undefined ? {} : { cwd: options.cwd }), + result: normalizedResult, + }); - for (const [key, value] of Object.entries({ ...this.env, ...options.env })) { - args.push("-e", `${key}=${value}`); - } + return normalizedResult; + }); + } - args.push(this.containerName, options.command, ...(options.args ?? [])); + async *execStream(options: SandboxExecOptions): AsyncIterable { + const events: SandboxExecStreamEvent[] = []; + let notify: (() => void) | undefined; + let done = false; + let error: unknown; - const cliOptions = { - dockerPath: this.dockerPath, - timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs, - maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes, - ...(options.input === undefined ? {} : { input: options.input }), - ...(options.signal === undefined ? {} : { signal: options.signal }), - ...(options.onStdout === undefined ? {} : { onStdout: options.onStdout }), - ...(options.onStderr === undefined ? {} : { onStderr: options.onStderr }), + const push = (event: SandboxExecStreamEvent) => { + events.push(event); + notify?.(); + notify = undefined; }; - const result = await runDockerCli(args, cliOptions); - - if (result.timedOut) { - return { - ...result, - exitCode: result.exitCode === 0 ? 124 : result.exitCode, - }; - } - - return result; - } + const wait = () => + new Promise((resolve) => { + notify = resolve; + }); - async readFile(filePath: string): Promise { - this.assertActive(); - const normalized = normalizeSandboxPath(filePath); - const tempDir = await mkdtemp(path.join(os.tmpdir(), "anvia-sandbox-read-")); - const target = path.join(tempDir, path.basename(normalized)); + const run = this.exec({ + ...options, + onStdout: (chunk) => { + options.onStdout?.(chunk); + push({ type: "stdout", chunk, text: Buffer.from(chunk).toString("utf8") }); + }, + onStderr: (chunk) => { + options.onStderr?.(chunk); + push({ type: "stderr", chunk, text: Buffer.from(chunk).toString("utf8") }); + }, + }) + .then((result) => { + push({ type: "exit", result }); + }) + .catch((caught) => { + error = caught; + }) + .finally(() => { + done = true; + notify?.(); + notify = undefined; + }); try { - await assertDockerCli( - ["cp", `${this.containerName}:${containerPath(this.workdir, normalized)}`, target], - this.cliOptions(), - ); - const { readFile } = await import("node:fs/promises"); - return await readFile(target); + while (!done || events.length > 0) { + const event = events.shift(); + if (event !== undefined) { + yield event; + continue; + } + + await wait(); + } + + if (error !== undefined) { + throw error; + } } finally { - await rm(tempDir, { recursive: true, force: true }); + await run; } } + async readFile(filePath: string): Promise { + return this.runOperation(async () => { + const normalized = normalizeSandboxPath(filePath); + const tempDir = await mkdtemp(path.join(os.tmpdir(), "anvia-sandbox-read-")); + const target = path.join(tempDir, path.basename(normalized)); + + try { + await assertDockerCli( + ["cp", `${this.containerName}:${containerPath(this.workdir, normalized)}`, target], + this.cliOptions(), + ); + const { readFile } = await import("node:fs/promises"); + const bytes = await readFile(target); + this.assertFileSize(bytes.byteLength, filePath); + return bytes; + } finally { + await rm(tempDir, { recursive: true, force: true }); + } + }); + } + async readTextFile(filePath: string): Promise { const bytes = await this.readFile(filePath); return new TextDecoder().decode(bytes); } async writeFile(filePath: string, data: string | Uint8Array): Promise { - this.assertActive(); - const normalized = normalizeSandboxPath(filePath); - await this.mkdir(parentSandboxPath(normalized)); - - const tempDir = await mkdtemp(path.join(os.tmpdir(), "anvia-sandbox-write-")); - const source = path.join(tempDir, path.basename(normalized)); - - try { - await writeFile(source, data); - await assertDockerCli( - ["cp", source, `${this.containerName}:${containerPath(this.workdir, normalized)}`], - this.cliOptions(), - ); - } finally { - await rm(tempDir, { recursive: true, force: true }); - } + await this.runOperation(async () => { + const size = byteLength(data); + this.assertFileSize(size, filePath); + const normalized = normalizeSandboxPath(filePath); + await this.mkdir(parentSandboxPath(normalized)); + + const tempDir = await mkdtemp(path.join(os.tmpdir(), "anvia-sandbox-write-")); + const source = path.join(tempDir, path.basename(normalized)); + + try { + await writeFile(source, data); + await assertDockerCli( + ["cp", source, `${this.containerName}:${containerPath(this.workdir, normalized)}`], + this.cliOptions(), + ); + await this.hooks.onFileWrite?.({ ...this.event(), path: normalized, size }); + } finally { + await rm(tempDir, { recursive: true, force: true }); + } + }); } async writeTextFile(filePath: string, content: string): Promise { @@ -340,26 +446,27 @@ class DockerSandboxSession implements SandboxSession { } async listFiles(filePath = "."): Promise { - this.assertActive(); - const normalized = normalizeSandboxPath(filePath, { allowRoot: true }); - const target = containerPath(this.workdir, normalized); - const result = await this.exec({ - command: "find", - args: [target, "-mindepth", "1", "-maxdepth", "1", "-printf", "%p\t%y\t%s\n"], - }); + return this.runOperation(async () => { + const normalized = normalizeSandboxPath(filePath, { allowRoot: true }); + const target = containerPath(this.workdir, normalized); + const result = await this.execCommand({ + command: "find", + args: [target, "-mindepth", "1", "-maxdepth", "1", "-printf", "%p\t%y\t%s\n"], + }); - if (result.timedOut) { - throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`); - } + if (result.timedOut) { + throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`); + } - if (result.exitCode !== 0) { - throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result); - } + if (result.exitCode !== 0) { + throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result); + } - return result.stdout - .split("\n") - .filter((line) => line.trim().length > 0) - .map((line) => this.parseFindEntry(line)); + return result.stdout + .split("\n") + .filter((line) => line.trim().length > 0) + .map((line) => this.parseFindEntry(line)); + }); } async destroy(): Promise { @@ -368,15 +475,21 @@ class DockerSandboxSession implements SandboxSession { } this.destroyed = true; + this.clearLifecycleTimers(); await runDockerCli(["rm", "-f", this.containerName], this.cliOptions()).catch(() => undefined); - await runDockerCli(["volume", "rm", "-f", this.volumeName], this.cliOptions()).catch( - () => undefined, - ); + + if (this.removeVolumeOnDestroy) { + await runDockerCli(["volume", "rm", "-f", this.volumeName], this.cliOptions()).catch( + () => undefined, + ); + } + + await this.hooks.onDestroy?.(this.event()); } private async mkdir(directoryPath: string): Promise { const normalized = normalizeSandboxPath(directoryPath, { allowRoot: true }); - const result = await this.exec({ + const result = await this.execCommand({ command: "mkdir", args: ["-p", containerPath(this.workdir, normalized)], }); @@ -415,11 +528,148 @@ class DockerSandboxSession implements SandboxSession { }; } + private async execCommand(options: SandboxExecOptions): Promise { + if (options.command.trim().length === 0) { + throw new SandboxDockerCommandError("Sandbox command cannot be empty.", { + stdout: "", + stderr: "", + exitCode: 1, + }); + } + + const args = ["exec"]; + + if (options.input !== undefined) { + args.push("-i"); + } + + const cwd = containerPath(this.workdir, options.cwd ?? "."); + args.push("-w", cwd); + + for (const [key, value] of Object.entries({ ...this.env, ...options.env })) { + args.push("-e", `${key}=${value}`); + } + + args.push(this.containerName, options.command, ...(options.args ?? [])); + + const cliOptions = { + dockerPath: this.dockerPath, + timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs, + maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes, + ...(options.input === undefined ? {} : { input: options.input }), + ...(options.signal === undefined ? {} : { signal: options.signal }), + ...(options.onStdout === undefined ? {} : { onStdout: options.onStdout }), + ...(options.onStderr === undefined ? {} : { onStderr: options.onStderr }), + }; + + const result = await runDockerCli(args, cliOptions); + + if (result.timedOut) { + return { + ...result, + exitCode: result.exitCode === 0 ? 124 : result.exitCode, + }; + } + + return result; + } + private assertActive(): void { if (this.destroyed) { throw new SandboxSessionDestroyedError(`Sandbox session ${this.id} has been destroyed.`); } } + + event(): SandboxSessionEvent { + return { + sessionId: this.id, + provider: this.provider, + workdir: this.workdir, + }; + } + + private async runOperation(operation: () => Promise): Promise { + this.assertActive(); + this.activeOperations += 1; + this.clearIdleTimer(); + + try { + return await operation(); + } finally { + this.activeOperations -= 1; + this.scheduleIdleTimer(); + } + } + + private assertFileSize(size: number, filePath: string): void { + if (this.limits.maxFileBytes !== undefined && size > this.limits.maxFileBytes) { + throw new SandboxFileSizeError( + `Sandbox file exceeds maxFileBytes (${size} > ${this.limits.maxFileBytes}): ${filePath}`, + ); + } + } + + private startLifecycleTimers(): void { + if (!this.lifecycle.autoDestroy) { + return; + } + + if (this.lifecycle.ttlMs !== undefined) { + this.ttlTimer = setTimeout(() => { + void this.destroy().catch(() => undefined); + }, this.lifecycle.ttlMs); + this.ttlTimer.unref?.(); + } + + this.scheduleIdleTimer(); + } + + private scheduleIdleTimer(): void { + if (!this.lifecycle.autoDestroy || this.lifecycle.idleTimeoutMs === undefined) { + return; + } + + if (this.destroyed || this.activeOperations > 0) { + return; + } + + this.clearIdleTimer(); + this.idleTimer = setTimeout(() => { + void this.destroy().catch(() => undefined); + }, this.lifecycle.idleTimeoutMs); + this.idleTimer.unref?.(); + } + + private clearLifecycleTimers(): void { + if (this.ttlTimer !== undefined) { + clearTimeout(this.ttlTimer); + this.ttlTimer = undefined; + } + this.clearIdleTimer(); + } + + private clearIdleTimer(): void { + if (this.idleTimer !== undefined) { + clearTimeout(this.idleTimer); + this.idleTimer = undefined; + } + } +} + +function getWorkspaceId(workspace: SandboxWorkspaceOptions, sessionId: string): string { + if (workspace.mode === "persistent") { + return sanitizeResourceId(workspace.id); + } + + return sessionId; +} + +function shouldDestroyWorkspace(workspace: SandboxWorkspaceOptions): boolean { + if (workspace.mode === "persistent") { + return workspace.destroyOnSessionDestroy ?? false; + } + + return true; } function sanitizeResourceId(id: string): string { @@ -430,6 +680,10 @@ function sanitizeResourceId(id: string): string { return sanitized.length > 0 ? sanitized : randomUUID(); } +function byteLength(data: string | Uint8Array): number { + return typeof data === "string" ? Buffer.byteLength(data) : data.byteLength; +} + function mapFindType(type: string | undefined): SandboxFileType { if (type === "f") { return "file"; diff --git a/packages/tools/sandbox/src/errors.ts b/packages/tools/sandbox/src/errors.ts index 5a7efbba..70aa88c2 100644 --- a/packages/tools/sandbox/src/errors.ts +++ b/packages/tools/sandbox/src/errors.ts @@ -28,3 +28,7 @@ export class SandboxSessionDestroyedError extends SandboxError {} export class SandboxPathError extends SandboxError {} export class SandboxTimeoutError extends SandboxError {} + +export class SandboxFileSizeError extends SandboxError {} + +export class SandboxToolPolicyError extends SandboxError {} diff --git a/packages/tools/sandbox/src/tools.ts b/packages/tools/sandbox/src/tools.ts index e5ecea0d..193e4126 100644 --- a/packages/tools/sandbox/src/tools.ts +++ b/packages/tools/sandbox/src/tools.ts @@ -1,5 +1,6 @@ import { type AnyTool, createTool } from "@anvia/core/tool"; import { z } from "zod"; +import { SandboxToolPolicyError } from "./errors"; import type { SandboxExecOptions, SandboxExecResult, @@ -49,7 +50,7 @@ export function createSandboxTools( options: SandboxToolsOptions = {}, ): AnyTool[] { const include = new Set( - options.include ?? ["exec_command", "read_file", "write_file", "list_files"], + options.allow ?? options.include ?? ["exec_command", "read_file", "write_file", "list_files"], ); const tools: AnyTool[] = []; @@ -58,11 +59,11 @@ export function createSandboxTools( } if (include.has("read_file")) { - tools.push(createReadFileTool(session)); + tools.push(createReadFileTool(session, options)); } if (include.has("write_file")) { - tools.push(createWriteFileTool(session)); + tools.push(createWriteFileTool(session, options)); } if (include.has("list_files")) { @@ -73,6 +74,8 @@ export function createSandboxTools( } function createExecCommandTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool { + const policy = options.exec ?? {}; + return createTool({ name: "exec_command", description: @@ -80,6 +83,8 @@ function createExecCommandTool(session: SandboxSession, options: SandboxToolsOpt input: execCommandInput, output: textOutput, execute: async ({ command, args, cwd, env, timeoutMs, input }) => { + assertCommandAllowed(command, options); + const execOptions: SandboxExecOptions = { command, }; @@ -93,8 +98,9 @@ function createExecCommandTool(session: SandboxSession, options: SandboxToolsOpt if (env !== undefined) { execOptions.env = env; } - const effectiveTimeoutMs = timeoutMs ?? options.execTimeoutMs; + const effectiveTimeoutMs = timeoutMs ?? policy.defaultTimeoutMs ?? options.execTimeoutMs; if (effectiveTimeoutMs !== undefined) { + assertTimeoutAllowed(effectiveTimeoutMs, options); execOptions.timeoutMs = effectiveTimeoutMs; } if (input !== undefined) { @@ -108,23 +114,28 @@ function createExecCommandTool(session: SandboxSession, options: SandboxToolsOpt }); } -function createReadFileTool(session: SandboxSession): AnyTool { +function createReadFileTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool { return createTool({ name: "read_file", description: "Read a text file from the sandbox workspace.", input: readFileInput, output: textOutput, - execute: async ({ path }) => session.readTextFile(path), + execute: async ({ path }) => { + const content = await session.readTextFile(path); + assertReadAllowed(content, options); + return content; + }, }); } -function createWriteFileTool(session: SandboxSession): AnyTool { +function createWriteFileTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool { return createTool({ name: "write_file", description: "Write a text file inside the sandbox workspace. Creates parent directories.", input: writeFileInput, output: textOutput, execute: async ({ path, content }) => { + assertContentAllowed(content, options); await session.writeTextFile(path, content); return `Wrote ${path}`; }, @@ -179,3 +190,41 @@ function formatExecResult(result: SandboxExecResult): string { return parts.join("\n\n"); } + +function assertCommandAllowed(command: string, options: SandboxToolsOptions): void { + const policy = options.exec; + + if (policy?.blockedCommands?.includes(command)) { + throw new SandboxToolPolicyError(`Command is blocked by sandbox tool policy: ${command}`); + } + + if (policy?.allowedCommands !== undefined && !policy.allowedCommands.includes(command)) { + throw new SandboxToolPolicyError(`Command is not allowed by sandbox tool policy: ${command}`); + } +} + +function assertTimeoutAllowed(timeoutMs: number, options: SandboxToolsOptions): void { + const maxTimeoutMs = options.exec?.maxTimeoutMs; + + if (maxTimeoutMs !== undefined && timeoutMs > maxTimeoutMs) { + throw new SandboxToolPolicyError( + `Command timeout exceeds sandbox tool policy (${timeoutMs} > ${maxTimeoutMs}).`, + ); + } +} + +function assertContentAllowed(content: string, options: SandboxToolsOptions): void { + const maxBytes = options.writeFile?.maxBytes; + + if (maxBytes !== undefined && Buffer.byteLength(content) > maxBytes) { + throw new SandboxToolPolicyError("File content exceeds sandbox tool policy."); + } +} + +function assertReadAllowed(content: string, options: SandboxToolsOptions): void { + const maxBytes = options.readFile?.maxBytes; + + if (maxBytes !== undefined && Buffer.byteLength(content) > maxBytes) { + throw new SandboxToolPolicyError("File content exceeds sandbox tool policy."); + } +} diff --git a/packages/tools/sandbox/src/types.ts b/packages/tools/sandbox/src/types.ts index 0e566555..8658f0d7 100644 --- a/packages/tools/sandbox/src/types.ts +++ b/packages/tools/sandbox/src/types.ts @@ -4,6 +4,22 @@ export type SandboxFileType = "file" | "directory" | "symlink" | "other"; export type SandboxNetworkMode = boolean | "none" | "host" | string; +export type SandboxWorkspaceOptions = + | { + mode?: "ephemeral"; + } + | { + mode: "persistent"; + id: string; + destroyOnSessionDestroy?: boolean; + }; + +export interface SandboxLifecycleOptions { + ttlMs?: number; + idleTimeoutMs?: number; + autoDestroy?: boolean; +} + export interface Sandbox { readonly provider: string; @@ -16,6 +32,7 @@ export interface SandboxSession { readonly workdir: string; exec(options: SandboxExecOptions): Promise; + execStream(options: SandboxExecOptions): AsyncIterable; readFile(path: string): Promise; readTextFile(path: string): Promise; writeFile(path: string, data: string | Uint8Array): Promise; @@ -26,6 +43,7 @@ export interface SandboxSession { export interface SandboxCreateSessionOptions { id?: string; + workspace?: SandboxWorkspaceOptions; manifest?: SandboxManifest; metadata?: Record; } @@ -39,6 +57,7 @@ export interface SandboxManifest { export interface SandboxLimits { timeoutMs?: number; maxOutputBytes?: number; + maxFileBytes?: number; memoryMb?: number; cpus?: number; pidsLimit?: number; @@ -67,6 +86,22 @@ export interface SandboxExecResult { stderrTruncated: boolean; } +export type SandboxExecStreamEvent = + | { + type: "stdout"; + chunk: Uint8Array; + text: string; + } + | { + type: "stderr"; + chunk: Uint8Array; + text: string; + } + | { + type: "exit"; + result: SandboxExecResult; + }; + export interface SandboxFileEntry { path: string; type: SandboxFileType; @@ -79,25 +114,76 @@ export interface DockerSandboxSecurityOptions { dropCapabilities?: string[]; } +export interface DockerSandboxNetworkOptions { + mode: SandboxNetworkMode; +} + +export interface SandboxSessionEvent { + sessionId: string; + provider: string; + workdir: string; +} + +export interface SandboxExecEvent extends SandboxSessionEvent { + command: string; + args: string[]; + cwd?: string; +} + +export interface SandboxExecEndEvent extends SandboxExecEvent { + result: SandboxExecResult; +} + +export interface SandboxFileWriteEvent extends SandboxSessionEvent { + path: string; + size: number; +} + +export interface SandboxHooks { + onSessionCreate?: (event: SandboxSessionEvent) => void | Promise; + onExecStart?: (event: SandboxExecEvent) => void | Promise; + onExecEnd?: (event: SandboxExecEndEvent) => void | Promise; + onFileWrite?: (event: SandboxFileWriteEvent) => void | Promise; + onDestroy?: (event: SandboxSessionEvent) => void | Promise; +} + export interface DockerSandboxOptions { image?: string; pull?: "missing" | "always" | "never"; workdir?: string; - network?: SandboxNetworkMode; + workspace?: SandboxWorkspaceOptions; + lifecycle?: SandboxLifecycleOptions; + network?: SandboxNetworkMode | DockerSandboxNetworkOptions; user?: string; dockerPath?: string; labels?: Record; limits?: SandboxLimits; security?: DockerSandboxSecurityOptions; + hooks?: SandboxHooks; } export interface SandboxToolsOptions { + allow?: SandboxToolName[]; include?: SandboxToolName[]; execTimeoutMs?: number; + exec?: SandboxExecToolPolicy; + readFile?: SandboxFileToolPolicy; + writeFile?: SandboxFileToolPolicy; } export type SandboxToolName = "exec_command" | "read_file" | "write_file" | "list_files"; +export interface SandboxExecToolPolicy { + allowedCommands?: string[]; + blockedCommands?: string[]; + defaultTimeoutMs?: number; + maxTimeoutMs?: number; +} + +export interface SandboxFileToolPolicy { + maxBytes?: number; +} + export type SandboxToolsFactory = ( session: SandboxSession, options?: SandboxToolsOptions, diff --git a/packages/tools/sandbox/test/docker-sandbox.integration.test.ts b/packages/tools/sandbox/test/docker-sandbox.integration.test.ts index 6d58b5b1..bcb7bc05 100644 --- a/packages/tools/sandbox/test/docker-sandbox.integration.test.ts +++ b/packages/tools/sandbox/test/docker-sandbox.integration.test.ts @@ -62,4 +62,106 @@ describe.skipIf(!runDockerTests)("DockerSandbox integration", () => { await session.destroy(); } }, 60_000); + + it("streams command output and enforces file size limits", async () => { + const sandbox = new DockerSandbox({ + image: "node:22-bookworm", + pull: "missing", + limits: { + timeoutMs: 10_000, + maxFileBytes: 4, + }, + }); + const session = await sandbox.createSession({ id: `stream-${Date.now()}` }); + + try { + const events = []; + + for await (const event of session.execStream({ + command: "node", + args: ["-e", "console.log('one'); console.error('two')"], + })) { + events.push(event); + } + + expect(events.map((event) => event.type)).toEqual(["stdout", "stderr", "exit"]); + expect(events.find((event) => event.type === "stdout")?.text.trim()).toBe("one"); + expect(events.find((event) => event.type === "stderr")?.text.trim()).toBe("two"); + await expect(session.writeTextFile("too-large.txt", "12345")).rejects.toThrow("maxFileBytes"); + } finally { + await session.destroy(); + } + }, 60_000); + + it("emits hooks for public sandbox operations", async () => { + const events: string[] = []; + const sandbox = new DockerSandbox({ + image: "node:22-bookworm", + pull: "missing", + hooks: { + onSessionCreate: (event) => { + events.push(`create:${event.sessionId}`); + }, + onExecStart: (event) => { + events.push(`exec:start:${event.command}`); + }, + onExecEnd: (event) => { + events.push(`exec:end:${event.result.exitCode}`); + }, + onFileWrite: (event) => { + events.push(`write:${event.path}:${event.size}`); + }, + onDestroy: (event) => { + events.push(`destroy:${event.sessionId}`); + }, + }, + }); + const session = await sandbox.createSession({ id: `hooks-${Date.now()}` }); + + await session.writeTextFile("out/result.txt", "ok"); + await session.listFiles("out"); + await session.exec({ command: "node", args: ["-e", "console.log('hook')"] }); + await session.destroy(); + + expect(events).toEqual([ + `create:${session.id}`, + "write:out/result.txt:2", + "exec:start:node", + "exec:end:0", + `destroy:${session.id}`, + ]); + }, 60_000); + + it("can reuse an explicit persistent workspace", async () => { + const workspaceId = `vitest-persistent-${Date.now()}`; + const sandbox = new DockerSandbox({ + image: "node:22-bookworm", + pull: "missing", + }); + const first = await sandbox.createSession({ + id: `${workspaceId}-first`, + workspace: { + mode: "persistent", + id: workspaceId, + }, + }); + + await first.writeTextFile("state.txt", "kept"); + await first.destroy(); + + const second = await sandbox.createSession({ + id: `${workspaceId}-second`, + workspace: { + mode: "persistent", + id: workspaceId, + destroyOnSessionDestroy: true, + }, + }); + + try { + await expect(second.readTextFile("state.txt")).resolves.toBe("kept"); + } finally { + await second.destroy(); + } + }, 60_000); }); diff --git a/packages/tools/sandbox/test/tools.test.ts b/packages/tools/sandbox/test/tools.test.ts index 1545be44..bc022caf 100644 --- a/packages/tools/sandbox/test/tools.test.ts +++ b/packages/tools/sandbox/test/tools.test.ts @@ -20,6 +20,12 @@ describe("createSandboxTools", () => { expect(tools.map((tool) => tool.name)).toEqual(["read_file"]); }); + it("supports allow as a tool selection alias", () => { + const tools = createSandboxTools(createSession(), { allow: ["list_files"] }); + + expect(tools.map((tool) => tool.name)).toEqual(["list_files"]); + }); + it("exec_command calls the sandbox session with structured args", async () => { const session = createSession(); const [tool] = createSandboxTools(session, { include: ["exec_command"], execTimeoutMs: 1234 }); @@ -45,6 +51,44 @@ describe("createSandboxTools", () => { expect(output).toContain("stdout:"); }); + it("enforces exec command policy", async () => { + const session = createSession(); + const [tool] = createSandboxTools(session, { + allow: ["exec_command"], + exec: { + allowedCommands: ["node"], + maxTimeoutMs: 1000, + }, + }); + if (tool === undefined) { + throw new Error("Expected exec_command tool."); + } + + await expect(tool.call({ command: "python" })).rejects.toThrow( + "Command is not allowed by sandbox tool policy", + ); + await expect(tool.call({ command: "node", timeoutMs: 2000 })).rejects.toThrow( + "Command timeout exceeds sandbox tool policy", + ); + }); + + it("enforces file tool byte policies", async () => { + const session = createSession(); + const tools = Object.fromEntries( + createSandboxTools(session, { + readFile: { maxBytes: 4 }, + writeFile: { maxBytes: 4 }, + }).map((tool) => [tool.name, tool] as const), + ); + + await expect(tools.read_file?.call({ path: "a.txt" })).rejects.toThrow( + "File content exceeds sandbox tool policy", + ); + await expect(tools.write_file?.call({ path: "a.txt", content: "content" })).rejects.toThrow( + "File content exceeds sandbox tool policy", + ); + }); + it("read_file, write_file, and list_files delegate to the session", async () => { const session = createSession(); const tools = Object.fromEntries( @@ -78,6 +122,21 @@ function createSession(): SandboxSession { stdoutTruncated: false, stderrTruncated: false, })), + execStream: vi.fn(async function* () { + yield { + type: "exit" as const, + result: { + stdout: "ok\n", + stderr: "", + exitCode: 0, + durationMs: 10, + timedOut: false, + aborted: false, + stdoutTruncated: false, + stderrTruncated: false, + }, + }; + }), readFile: vi.fn(async () => new TextEncoder().encode("file content")), readTextFile: vi.fn(async () => "file content"), writeFile: vi.fn(async () => undefined), From b3d12aa854e1d4411a6c27942b17caf39e349a30 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:17:52 +0700 Subject: [PATCH 05/16] Version Packages (#106) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/warm-sandboxes-stream.md | 5 ----- packages/tools/sandbox/CHANGELOG.md | 6 ++++++ packages/tools/sandbox/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/warm-sandboxes-stream.md diff --git a/.changeset/warm-sandboxes-stream.md b/.changeset/warm-sandboxes-stream.md deleted file mode 100644 index ea721fea..00000000 --- a/.changeset/warm-sandboxes-stream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@anvia/sandbox": minor ---- - -Add sandbox V2 controls for persistent workspaces, lifecycle cleanup, streaming command execution, file-size limits, observability hooks, language presets, and model-facing tool policies. diff --git a/packages/tools/sandbox/CHANGELOG.md b/packages/tools/sandbox/CHANGELOG.md index 323a9f48..5f694f7c 100644 --- a/packages/tools/sandbox/CHANGELOG.md +++ b/packages/tools/sandbox/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/sandbox +## 0.3.0 + +### Minor Changes + +- 18403b7: Add sandbox V2 controls for persistent workspaces, lifecycle cleanup, streaming command execution, file-size limits, observability hooks, language presets, and model-facing tool policies. + ## 0.2.2 ### Patch Changes diff --git a/packages/tools/sandbox/package.json b/packages/tools/sandbox/package.json index 2d9c5843..487d9b13 100644 --- a/packages/tools/sandbox/package.json +++ b/packages/tools/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/sandbox", - "version": "0.2.2", + "version": "0.3.0", "description": "Sandboxed workspace tools for Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", From 3572881905039423e2d574c7735a91226830f9e0 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:47:39 +0700 Subject: [PATCH 06/16] chore: flatten package layout (#107) * chore: flatten package layout * docs: align package layout references --- .changeset/flat-package-layout.md | 19 ++++ CHANGESET_GUIDE.md | 8 +- CONTRIBUTING.md | 6 +- DEVELOPMENT.md | 14 +-- README.md | 2 +- .../docs/content/docs/changelog/anthropic.mdx | 2 +- apps/docs/content/docs/changelog/chroma.mdx | 2 +- .../docs/content/docs/changelog/fastembed.mdx | 2 +- apps/docs/content/docs/changelog/gemini.mdx | 2 +- apps/docs/content/docs/changelog/index.mdx | 2 +- apps/docs/content/docs/changelog/langfuse.mdx | 2 +- apps/docs/content/docs/changelog/milvus.mdx | 2 +- apps/docs/content/docs/changelog/mistral.mdx | 2 +- apps/docs/content/docs/changelog/openai.mdx | 2 +- apps/docs/content/docs/changelog/otel.mdx | 2 +- apps/docs/content/docs/changelog/pgvector.mdx | 2 +- apps/docs/content/docs/changelog/pinecone.mdx | 2 +- apps/docs/content/docs/changelog/qdrant.mdx | 2 +- apps/docs/content/docs/changelog/sandbox.mdx | 8 +- apps/docs/content/docs/changelog/studio.mdx | 2 +- .../content/docs/changelog/transformers.mdx | 2 +- .../docs/scripts/check-reference-coverage.mjs | 21 ++-- apps/docs/scripts/generate-changelogs.mjs | 12 ++- apps/docs/src/routeTree.gen.ts | 9 ++ examples/cookbook/tsconfig.json | 22 ++--- package.json | 2 +- .../CHANGELOG.md | 0 .../README.md | 0 .../package.json | 2 +- .../src/index.ts | 0 .../test/embedding.test.ts | 0 .../tsconfig.json | 2 +- .../vitest.config.ts | 4 +- .../CHANGELOG.md | 0 .../README.md | 0 .../package.json | 2 +- .../src/index.ts | 0 .../test/embedding.test.ts | 0 .../tsconfig.json | 2 +- .../vitest.config.ts | 4 +- .../CHANGELOG.md | 0 .../README.md | 0 .../package.json | 2 +- .../src/index.ts | 0 .../test/langfuse.test.ts | 0 .../tsconfig.json | 2 +- .../observability-langfuse/vitest.config.ts | 17 ++++ .../otel => observability-otel}/CHANGELOG.md | 0 .../otel => observability-otel}/README.md | 0 .../otel => observability-otel}/package.json | 2 +- .../otel => observability-otel}/src/index.ts | 0 .../test/otel.test.ts | 0 .../otel => observability-otel}/tsconfig.json | 2 +- packages/observability-otel/vitest.config.ts | 16 ++++ .../observability/langfuse/vitest.config.ts | 17 ---- packages/observability/otel/vitest.config.ts | 16 ---- .../CHANGELOG.md | 0 .../README.md | 0 .../package.json | 6 +- .../src/anthropic/client.ts | 0 .../src/anthropic/completion.ts | 0 .../src/anthropic/index.ts | 0 .../src/index.ts | 0 .../src/request-messages.ts | 0 .../src/utils.ts | 0 .../test/anthropic-completion.test.ts | 0 .../test/client.test.ts | 0 packages/provider-anthropic/tsconfig.json | 7 ++ packages/provider-anthropic/vitest.config.ts | 18 ++++ .../gemini => provider-gemini}/CHANGELOG.md | 0 .../gemini => provider-gemini}/README.md | 0 .../gemini => provider-gemini}/package.json | 6 +- .../src/gemini/client.ts | 0 .../src/gemini/completion.ts | 0 .../src/gemini/embedding.ts | 0 .../src/gemini/image-generation.ts | 0 .../src/gemini/index.ts | 0 .../src/gemini/transcription.ts | 0 .../gemini => provider-gemini}/src/index.ts | 0 .../src/request-messages.ts | 0 .../test/client.test.ts | 0 .../test/completion.test.ts | 0 .../test/embedding.test.ts | 0 .../test/multimodal.test.ts | 0 packages/provider-gemini/tsconfig.json | 7 ++ packages/provider-gemini/vitest.config.ts | 28 ++++++ .../mistral => provider-mistral}/CHANGELOG.md | 0 .../mistral => provider-mistral}/README.md | 0 .../mistral => provider-mistral}/package.json | 6 +- .../mistral => provider-mistral}/src/index.ts | 0 .../src/mistral/client.ts | 0 .../src/mistral/completion.ts | 0 .../src/mistral/embedding.ts | 0 .../src/mistral/index.ts | 0 .../src/request-messages.ts | 0 .../mistral => provider-mistral}/src/utils.ts | 0 .../test/client.test.ts | 0 .../test/completion.test.ts | 0 .../test/embedding.test.ts | 0 packages/provider-mistral/tsconfig.json | 7 ++ packages/provider-mistral/vitest.config.ts | 18 ++++ .../openai => provider-openai}/CHANGELOG.md | 0 .../openai => provider-openai}/README.md | 0 .../openai => provider-openai}/package.json | 6 +- .../openai => provider-openai}/src/index.ts | 0 .../src/openai/audio-generation.ts | 0 .../src/openai/chat-completion.ts | 0 .../src/openai/client.ts | 0 .../src/openai/embedding.ts | 0 .../src/openai/image-generation.ts | 0 .../src/openai/index.ts | 0 .../src/openai/responses.ts | 0 .../src/openai/transcription.ts | 0 .../src/request-messages.ts | 0 .../openai => provider-openai}/src/utils.ts | 0 .../test/client.test.ts | 0 .../test/embedding.test.ts | 0 .../test/multimodal.test.ts | 0 .../test/openai-chat-completion.test.ts | 0 .../test/openai-responses.test.ts | 0 packages/provider-openai/tsconfig.json | 7 ++ packages/provider-openai/vitest.config.ts | 28 ++++++ packages/providers/anthropic/tsconfig.json | 7 -- packages/providers/anthropic/vitest.config.ts | 18 ---- packages/providers/gemini/tsconfig.json | 7 -- packages/providers/gemini/vitest.config.ts | 28 ------ packages/providers/mistral/tsconfig.json | 7 -- packages/providers/mistral/vitest.config.ts | 18 ---- packages/providers/openai/tsconfig.json | 7 -- packages/providers/openai/vitest.config.ts | 28 ------ .../sandbox => tool-sandbox}/CHANGELOG.md | 0 .../sandbox => tool-sandbox}/package.json | 4 +- .../src/docker-cli.ts | 0 .../src/docker-sandbox.ts | 0 .../sandbox => tool-sandbox}/src/errors.ts | 0 .../sandbox => tool-sandbox}/src/index.ts | 0 .../sandbox => tool-sandbox}/src/path.ts | 0 .../sandbox => tool-sandbox}/src/tools.ts | 0 .../sandbox => tool-sandbox}/src/types.ts | 0 .../test/docker-sandbox.integration.test.ts | 0 .../test/path.test.ts | 0 .../test/tools.test.ts | 0 packages/tool-sandbox/tsconfig.json | 7 ++ packages/tool-sandbox/vitest.config.ts | 13 +++ .../studio => tool-studio}/CHANGELOG.md | 0 .../{tools/studio => tool-studio}/README.md | 0 .../studio => tool-studio}/components.json | 0 .../studio => tool-studio}/package.json | 4 +- .../studio => tool-studio}/src/index.ts | 0 .../studio => tool-studio}/src/runner.ts | 0 .../src/runtime/approvals.ts | 0 .../src/runtime/evals.ts | 0 .../src/runtime/json.ts | 0 .../src/runtime/knowledge.ts | 0 .../src/runtime/mcps.ts | 0 .../src/runtime/memory.ts | 0 .../src/runtime/observability.ts | 0 .../src/runtime/pipeline-logs.ts | 0 .../src/runtime/pipelines.ts | 0 .../src/runtime/questions.ts | 0 .../src/runtime/runs.ts | 0 .../src/runtime/session-logs.ts | 0 .../src/runtime/sessions.ts | 0 .../src/runtime/shared.ts | 0 .../src/runtime/status.ts | 0 .../src/runtime/streams.ts | 0 .../src/runtime/studio.ts | 0 .../src/runtime/tool-metadata.ts | 0 .../src/runtime/tools.ts | 0 .../src/runtime/trace-routes.ts | 0 .../src/runtime/transcript.ts | 0 .../studio => tool-studio}/src/sqlite.ts | 0 .../src/storage/memory-store.ts | 0 .../src/storage/sqlite-store.ts | 0 .../studio => tool-studio}/src/trace.ts | 0 .../src/traces/trace-observer.ts | 0 .../studio => tool-studio}/src/types.ts | 0 .../studio => tool-studio}/src/ui/app/app.tsx | 0 .../src/ui/app/components/ui/badge.tsx | 0 .../src/ui/app/components/ui/button.tsx | 0 .../src/ui/app/components/ui/card.tsx | 0 .../src/ui/app/components/ui/dialog.tsx | 0 .../src/ui/app/components/ui/input.tsx | 0 .../src/ui/app/components/ui/scroll-area.tsx | 0 .../src/ui/app/components/ui/select.tsx | 0 .../src/ui/app/components/ui/separator.tsx | 0 .../src/ui/app/components/ui/textarea.tsx | 0 .../src/ui/app/index.html | 0 .../src/ui/app/lib/utils.ts | 0 .../src/ui/app/main.tsx | 0 .../src/ui/app/modules/agents/agents-page.tsx | 0 .../src/ui/app/modules/evals/evals-page.tsx | 0 .../app/modules/knowledge/knowledge-page.tsx | 0 .../src/ui/app/modules/mcps/mcps-page.tsx | 0 .../src/ui/app/modules/memory/memory-page.tsx | 0 .../modules/pipelines/pipeline-logs-panel.tsx | 0 .../app/modules/pipelines/pipelines-page.tsx | 0 .../modules/playground/transcript-item.tsx | 0 .../session-logs/session-logs-panel.tsx | 0 .../ui/app/modules/sessions/sessions-page.tsx | 0 .../src/ui/app/modules/shared/format.ts | 0 .../src/ui/app/modules/shared/navigation.ts | 0 .../src/ui/app/modules/shared/object.ts | 0 .../src/ui/app/modules/shared/path.ts | 0 .../src/ui/app/modules/shared/renderers.tsx | 0 .../src/ui/app/modules/shared/transcript.ts | 0 .../src/ui/app/modules/shared/types.ts | 0 .../src/ui/app/modules/shell/nav-button.tsx | 0 .../src/ui/app/modules/status/status-page.tsx | 0 .../src/ui/app/modules/tools/tools-page.tsx | 0 .../ui/app/modules/tracing/trace-browser.tsx | 0 .../src/ui/app/styles.css | 0 .../src/ui/assets/anvia.png | Bin .../src/ui/assets/logo.png | Bin .../src/ui/public/assets/anvia.png | Bin .../src/ui/public/assets/logo.png | Bin .../studio => tool-studio}/src/ui/routes.tsx | 0 .../test/runner.test.ts | 0 .../test/studio-ui-helpers.test.ts | 0 .../test/trace-browser.test.ts | 0 .../studio => tool-studio}/tsconfig.json | 2 +- .../studio => tool-studio}/tsup.config.ts | 0 .../studio => tool-studio}/vite.config.ts | 0 packages/tool-studio/vitest.config.ts | 29 ++++++ packages/tools/sandbox/tsconfig.json | 7 -- packages/tools/sandbox/vitest.config.ts | 13 --- packages/tools/studio/vitest.config.ts | 29 ------ .../chroma => vector-chroma}/CHANGELOG.md | 0 .../chroma => vector-chroma}/README.md | 0 .../chroma => vector-chroma}/package.json | 2 +- .../chroma => vector-chroma}/src/filters.ts | 0 .../chroma => vector-chroma}/src/helpers.ts | 0 .../chroma => vector-chroma}/src/index.ts | 0 .../src/search-index.ts | 0 .../chroma => vector-chroma}/src/store.ts | 0 .../chroma => vector-chroma}/src/types.ts | 0 .../test/chroma-vector-store.test.ts | 0 packages/vector-chroma/tsconfig.json | 7 ++ packages/vector-chroma/vitest.config.ts | 17 ++++ .../milvus => vector-milvus}/CHANGELOG.md | 0 .../milvus => vector-milvus}/package.json | 2 +- .../milvus => vector-milvus}/src/filters.ts | 0 .../milvus => vector-milvus}/src/helpers.ts | 0 .../milvus => vector-milvus}/src/index.ts | 0 .../src/search-index.ts | 0 .../milvus => vector-milvus}/src/store.ts | 0 .../milvus => vector-milvus}/src/types.ts | 0 .../test/milvus-vector-store.test.ts | 0 packages/vector-milvus/tsconfig.json | 7 ++ .../pgvector => vector-pgvector}/CHANGELOG.md | 0 .../pgvector => vector-pgvector}/README.md | 0 .../pgvector => vector-pgvector}/package.json | 2 +- .../src/filters.ts | 0 .../src/helpers.ts | 0 .../pgvector => vector-pgvector}/src/index.ts | 0 .../src/search-index.ts | 0 .../pgvector => vector-pgvector}/src/store.ts | 0 .../pgvector => vector-pgvector}/src/types.ts | 0 .../test/pgvector-store.test.ts | 0 packages/vector-pgvector/tsconfig.json | 7 ++ packages/vector-pgvector/vitest.config.ts | 17 ++++ .../pinecone => vector-pinecone}/CHANGELOG.md | 0 .../pinecone => vector-pinecone}/package.json | 2 +- .../src/filters.ts | 0 .../src/helpers.ts | 0 .../pinecone => vector-pinecone}/src/index.ts | 0 .../src/search-index.ts | 0 .../pinecone => vector-pinecone}/src/store.ts | 0 .../pinecone => vector-pinecone}/src/types.ts | 0 .../test/pinecone-vector-store.test.ts | 0 packages/vector-pinecone/tsconfig.json | 7 ++ .../qdrant => vector-qdrant}/CHANGELOG.md | 0 .../qdrant => vector-qdrant}/README.md | 0 .../qdrant => vector-qdrant}/package.json | 2 +- .../qdrant => vector-qdrant}/src/filters.ts | 0 .../qdrant => vector-qdrant}/src/helpers.ts | 0 .../qdrant => vector-qdrant}/src/index.ts | 0 .../src/search-index.ts | 0 .../qdrant => vector-qdrant}/src/store.ts | 0 .../qdrant => vector-qdrant}/src/types.ts | 0 .../test/qdrant-vector-store.test.ts | 0 packages/vector-qdrant/tsconfig.json | 7 ++ packages/vector-qdrant/vitest.config.ts | 17 ++++ packages/vector-stores/chroma/tsconfig.json | 7 -- .../vector-stores/chroma/vitest.config.ts | 17 ---- packages/vector-stores/milvus/tsconfig.json | 7 -- packages/vector-stores/pgvector/tsconfig.json | 7 -- .../vector-stores/pgvector/vitest.config.ts | 17 ---- packages/vector-stores/pinecone/tsconfig.json | 7 -- packages/vector-stores/qdrant/tsconfig.json | 7 -- .../vector-stores/qdrant/vitest.config.ts | 17 ---- pnpm-lock.yaml | 90 +++++++++--------- pnpm-workspace.yaml | 1 - 293 files changed, 462 insertions(+), 426 deletions(-) create mode 100644 .changeset/flat-package-layout.md rename packages/{embeddings/fastembed => embedding-fastembed}/CHANGELOG.md (100%) rename packages/{embeddings/fastembed => embedding-fastembed}/README.md (100%) rename packages/{embeddings/fastembed => embedding-fastembed}/package.json (94%) rename packages/{embeddings/fastembed => embedding-fastembed}/src/index.ts (100%) rename packages/{embeddings/fastembed => embedding-fastembed}/test/embedding.test.ts (100%) rename packages/{embeddings/transformers => embedding-fastembed}/tsconfig.json (69%) rename packages/{embeddings/transformers => embedding-fastembed}/vitest.config.ts (50%) rename packages/{embeddings/transformers => embedding-transformers}/CHANGELOG.md (100%) rename packages/{embeddings/transformers => embedding-transformers}/README.md (100%) rename packages/{embeddings/transformers => embedding-transformers}/package.json (94%) rename packages/{embeddings/transformers => embedding-transformers}/src/index.ts (100%) rename packages/{embeddings/transformers => embedding-transformers}/test/embedding.test.ts (100%) rename packages/{embeddings/fastembed => embedding-transformers}/tsconfig.json (69%) rename packages/{embeddings/fastembed => embedding-transformers}/vitest.config.ts (50%) rename packages/{observability/langfuse => observability-langfuse}/CHANGELOG.md (100%) rename packages/{observability/langfuse => observability-langfuse}/README.md (100%) rename packages/{observability/langfuse => observability-langfuse}/package.json (95%) rename packages/{observability/langfuse => observability-langfuse}/src/index.ts (100%) rename packages/{observability/langfuse => observability-langfuse}/test/langfuse.test.ts (100%) rename packages/{observability/langfuse => observability-langfuse}/tsconfig.json (69%) create mode 100644 packages/observability-langfuse/vitest.config.ts rename packages/{observability/otel => observability-otel}/CHANGELOG.md (100%) rename packages/{observability/otel => observability-otel}/README.md (100%) rename packages/{observability/otel => observability-otel}/package.json (95%) rename packages/{observability/otel => observability-otel}/src/index.ts (100%) rename packages/{observability/otel => observability-otel}/test/otel.test.ts (100%) rename packages/{observability/otel => observability-otel}/tsconfig.json (69%) create mode 100644 packages/observability-otel/vitest.config.ts delete mode 100644 packages/observability/langfuse/vitest.config.ts delete mode 100644 packages/observability/otel/vitest.config.ts rename packages/{providers/anthropic => provider-anthropic}/CHANGELOG.md (100%) rename packages/{providers/anthropic => provider-anthropic}/README.md (100%) rename packages/{providers/anthropic => provider-anthropic}/package.json (79%) rename packages/{providers/anthropic => provider-anthropic}/src/anthropic/client.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/src/anthropic/completion.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/src/anthropic/index.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/src/index.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/src/request-messages.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/src/utils.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/test/anthropic-completion.test.ts (100%) rename packages/{providers/anthropic => provider-anthropic}/test/client.test.ts (100%) create mode 100644 packages/provider-anthropic/tsconfig.json create mode 100644 packages/provider-anthropic/vitest.config.ts rename packages/{providers/gemini => provider-gemini}/CHANGELOG.md (100%) rename packages/{providers/gemini => provider-gemini}/README.md (100%) rename packages/{providers/gemini => provider-gemini}/package.json (79%) rename packages/{providers/gemini => provider-gemini}/src/gemini/client.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/gemini/completion.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/gemini/embedding.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/gemini/image-generation.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/gemini/index.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/gemini/transcription.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/index.ts (100%) rename packages/{providers/gemini => provider-gemini}/src/request-messages.ts (100%) rename packages/{providers/gemini => provider-gemini}/test/client.test.ts (100%) rename packages/{providers/gemini => provider-gemini}/test/completion.test.ts (100%) rename packages/{providers/gemini => provider-gemini}/test/embedding.test.ts (100%) rename packages/{providers/gemini => provider-gemini}/test/multimodal.test.ts (100%) create mode 100644 packages/provider-gemini/tsconfig.json create mode 100644 packages/provider-gemini/vitest.config.ts rename packages/{providers/mistral => provider-mistral}/CHANGELOG.md (100%) rename packages/{providers/mistral => provider-mistral}/README.md (100%) rename packages/{providers/mistral => provider-mistral}/package.json (80%) rename packages/{providers/mistral => provider-mistral}/src/index.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/mistral/client.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/mistral/completion.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/mistral/embedding.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/mistral/index.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/request-messages.ts (100%) rename packages/{providers/mistral => provider-mistral}/src/utils.ts (100%) rename packages/{providers/mistral => provider-mistral}/test/client.test.ts (100%) rename packages/{providers/mistral => provider-mistral}/test/completion.test.ts (100%) rename packages/{providers/mistral => provider-mistral}/test/embedding.test.ts (100%) create mode 100644 packages/provider-mistral/tsconfig.json create mode 100644 packages/provider-mistral/vitest.config.ts rename packages/{providers/openai => provider-openai}/CHANGELOG.md (100%) rename packages/{providers/openai => provider-openai}/README.md (100%) rename packages/{providers/openai => provider-openai}/package.json (79%) rename packages/{providers/openai => provider-openai}/src/index.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/audio-generation.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/chat-completion.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/client.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/embedding.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/image-generation.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/index.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/responses.ts (100%) rename packages/{providers/openai => provider-openai}/src/openai/transcription.ts (100%) rename packages/{providers/openai => provider-openai}/src/request-messages.ts (100%) rename packages/{providers/openai => provider-openai}/src/utils.ts (100%) rename packages/{providers/openai => provider-openai}/test/client.test.ts (100%) rename packages/{providers/openai => provider-openai}/test/embedding.test.ts (100%) rename packages/{providers/openai => provider-openai}/test/multimodal.test.ts (100%) rename packages/{providers/openai => provider-openai}/test/openai-chat-completion.test.ts (100%) rename packages/{providers/openai => provider-openai}/test/openai-responses.test.ts (100%) create mode 100644 packages/provider-openai/tsconfig.json create mode 100644 packages/provider-openai/vitest.config.ts delete mode 100644 packages/providers/anthropic/tsconfig.json delete mode 100644 packages/providers/anthropic/vitest.config.ts delete mode 100644 packages/providers/gemini/tsconfig.json delete mode 100644 packages/providers/gemini/vitest.config.ts delete mode 100644 packages/providers/mistral/tsconfig.json delete mode 100644 packages/providers/mistral/vitest.config.ts delete mode 100644 packages/providers/openai/tsconfig.json delete mode 100644 packages/providers/openai/vitest.config.ts rename packages/{tools/sandbox => tool-sandbox}/CHANGELOG.md (100%) rename packages/{tools/sandbox => tool-sandbox}/package.json (87%) rename packages/{tools/sandbox => tool-sandbox}/src/docker-cli.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/docker-sandbox.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/errors.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/index.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/path.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/tools.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/src/types.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/test/docker-sandbox.integration.test.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/test/path.test.ts (100%) rename packages/{tools/sandbox => tool-sandbox}/test/tools.test.ts (100%) create mode 100644 packages/tool-sandbox/tsconfig.json create mode 100644 packages/tool-sandbox/vitest.config.ts rename packages/{tools/studio => tool-studio}/CHANGELOG.md (100%) rename packages/{tools/studio => tool-studio}/README.md (100%) rename packages/{tools/studio => tool-studio}/components.json (100%) rename packages/{tools/studio => tool-studio}/package.json (90%) rename packages/{tools/studio => tool-studio}/src/index.ts (100%) rename packages/{tools/studio => tool-studio}/src/runner.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/approvals.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/evals.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/json.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/knowledge.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/mcps.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/memory.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/observability.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/pipeline-logs.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/pipelines.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/questions.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/runs.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/session-logs.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/sessions.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/shared.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/status.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/streams.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/studio.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/tool-metadata.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/tools.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/trace-routes.ts (100%) rename packages/{tools/studio => tool-studio}/src/runtime/transcript.ts (100%) rename packages/{tools/studio => tool-studio}/src/sqlite.ts (100%) rename packages/{tools/studio => tool-studio}/src/storage/memory-store.ts (100%) rename packages/{tools/studio => tool-studio}/src/storage/sqlite-store.ts (100%) rename packages/{tools/studio => tool-studio}/src/trace.ts (100%) rename packages/{tools/studio => tool-studio}/src/traces/trace-observer.ts (100%) rename packages/{tools/studio => tool-studio}/src/types.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/app.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/badge.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/button.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/card.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/dialog.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/input.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/scroll-area.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/select.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/separator.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/components/ui/textarea.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/index.html (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/lib/utils.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/main.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/agents/agents-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/evals/evals-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/knowledge/knowledge-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/mcps/mcps-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/memory/memory-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/pipelines/pipeline-logs-panel.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/pipelines/pipelines-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/playground/transcript-item.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/session-logs/session-logs-panel.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/sessions/sessions-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/format.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/navigation.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/object.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/path.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/renderers.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/transcript.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shared/types.ts (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/shell/nav-button.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/status/status-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/tools/tools-page.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/modules/tracing/trace-browser.tsx (100%) rename packages/{tools/studio => tool-studio}/src/ui/app/styles.css (100%) rename packages/{tools/studio => tool-studio}/src/ui/assets/anvia.png (100%) rename packages/{tools/studio => tool-studio}/src/ui/assets/logo.png (100%) rename packages/{tools/studio => tool-studio}/src/ui/public/assets/anvia.png (100%) rename packages/{tools/studio => tool-studio}/src/ui/public/assets/logo.png (100%) rename packages/{tools/studio => tool-studio}/src/ui/routes.tsx (100%) rename packages/{tools/studio => tool-studio}/test/runner.test.ts (100%) rename packages/{tools/studio => tool-studio}/test/studio-ui-helpers.test.ts (100%) rename packages/{tools/studio => tool-studio}/test/trace-browser.test.ts (100%) rename packages/{tools/studio => tool-studio}/tsconfig.json (86%) rename packages/{tools/studio => tool-studio}/tsup.config.ts (100%) rename packages/{tools/studio => tool-studio}/vite.config.ts (100%) create mode 100644 packages/tool-studio/vitest.config.ts delete mode 100644 packages/tools/sandbox/tsconfig.json delete mode 100644 packages/tools/sandbox/vitest.config.ts delete mode 100644 packages/tools/studio/vitest.config.ts rename packages/{vector-stores/chroma => vector-chroma}/CHANGELOG.md (100%) rename packages/{vector-stores/chroma => vector-chroma}/README.md (100%) rename packages/{vector-stores/chroma => vector-chroma}/package.json (94%) rename packages/{vector-stores/chroma => vector-chroma}/src/filters.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/src/helpers.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/src/index.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/src/search-index.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/src/store.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/src/types.ts (100%) rename packages/{vector-stores/chroma => vector-chroma}/test/chroma-vector-store.test.ts (100%) create mode 100644 packages/vector-chroma/tsconfig.json create mode 100644 packages/vector-chroma/vitest.config.ts rename packages/{vector-stores/milvus => vector-milvus}/CHANGELOG.md (100%) rename packages/{vector-stores/milvus => vector-milvus}/package.json (94%) rename packages/{vector-stores/milvus => vector-milvus}/src/filters.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/src/helpers.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/src/index.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/src/search-index.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/src/store.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/src/types.ts (100%) rename packages/{vector-stores/milvus => vector-milvus}/test/milvus-vector-store.test.ts (100%) create mode 100644 packages/vector-milvus/tsconfig.json rename packages/{vector-stores/pgvector => vector-pgvector}/CHANGELOG.md (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/README.md (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/package.json (94%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/filters.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/helpers.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/index.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/search-index.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/store.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/src/types.ts (100%) rename packages/{vector-stores/pgvector => vector-pgvector}/test/pgvector-store.test.ts (100%) create mode 100644 packages/vector-pgvector/tsconfig.json create mode 100644 packages/vector-pgvector/vitest.config.ts rename packages/{vector-stores/pinecone => vector-pinecone}/CHANGELOG.md (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/package.json (94%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/filters.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/helpers.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/index.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/search-index.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/store.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/src/types.ts (100%) rename packages/{vector-stores/pinecone => vector-pinecone}/test/pinecone-vector-store.test.ts (100%) create mode 100644 packages/vector-pinecone/tsconfig.json rename packages/{vector-stores/qdrant => vector-qdrant}/CHANGELOG.md (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/README.md (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/package.json (94%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/filters.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/helpers.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/index.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/search-index.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/store.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/src/types.ts (100%) rename packages/{vector-stores/qdrant => vector-qdrant}/test/qdrant-vector-store.test.ts (100%) create mode 100644 packages/vector-qdrant/tsconfig.json create mode 100644 packages/vector-qdrant/vitest.config.ts delete mode 100644 packages/vector-stores/chroma/tsconfig.json delete mode 100644 packages/vector-stores/chroma/vitest.config.ts delete mode 100644 packages/vector-stores/milvus/tsconfig.json delete mode 100644 packages/vector-stores/pgvector/tsconfig.json delete mode 100644 packages/vector-stores/pgvector/vitest.config.ts delete mode 100644 packages/vector-stores/pinecone/tsconfig.json delete mode 100644 packages/vector-stores/qdrant/tsconfig.json delete mode 100644 packages/vector-stores/qdrant/vitest.config.ts diff --git a/.changeset/flat-package-layout.md b/.changeset/flat-package-layout.md new file mode 100644 index 00000000..5eaa1611 --- /dev/null +++ b/.changeset/flat-package-layout.md @@ -0,0 +1,19 @@ +--- +"@anvia/anthropic": patch +"@anvia/chroma": patch +"@anvia/fastembed": patch +"@anvia/gemini": patch +"@anvia/langfuse": patch +"@anvia/milvus": patch +"@anvia/mistral": patch +"@anvia/openai": patch +"@anvia/otel": patch +"@anvia/pgvector": patch +"@anvia/pinecone": patch +"@anvia/qdrant": patch +"@anvia/sandbox": patch +"@anvia/studio": patch +"@anvia/transformers": patch +--- + +Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. diff --git a/CHANGESET_GUIDE.md b/CHANGESET_GUIDE.md index fe288098..fa6ffc7e 100644 --- a/CHANGESET_GUIDE.md +++ b/CHANGESET_GUIDE.md @@ -44,9 +44,9 @@ Run the relevant checks: ```sh pnpm install --frozen-lockfile -pnpm --filter './packages/**' typecheck -pnpm --filter './packages/**' test -pnpm --filter './packages/**' build +pnpm --filter './packages/*' typecheck +pnpm --filter './packages/*' test +pnpm --filter './packages/*' build ``` If docs changed, also run: @@ -96,7 +96,7 @@ pnpm release That command runs: ```sh -pnpm --filter './packages/**' build && changeset publish +pnpm --filter './packages/*' build && changeset publish ``` ## Manual Version Check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4c69fd6..1a76300d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,9 +13,9 @@ Thanks for taking the time to improve Anvia. This project is a TypeScript pnpm w Good contributions usually fall into one of these areas: - Core runtime fixes or focused feature work in `packages/core`. -- Provider adapter fixes in `packages/providers/*`. -- Vector store or embedding adapter fixes in `packages/vector-stores/*` and `packages/embeddings/*`. -- Studio runtime or UI fixes in `packages/tools/studio`. +- Provider adapter fixes in `packages/provider-*`. +- Vector store or embedding adapter fixes in `packages/vector-*` and `packages/embedding-*`. +- Studio runtime or UI fixes in `packages/tool-studio`. - Cookbook examples that clarify an existing workflow or introduce a missing one. - Documentation updates in `apps/docs` or package READMEs. - Tests that capture bugs, edge cases, or public behavior. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c626e7d0..892d97a4 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -25,18 +25,20 @@ pnpm install │ └── cookbook/ # Runnable learning path ├── packages/ │ ├── core/ # @anvia/core -│ ├── providers/ # Provider adapters -│ ├── embeddings/ # Embedding adapters -│ ├── observability/ # Observability adapters -│ ├── tools/studio/ # @anvia/studio -│ └── vector-stores/ # Vector store adapters +│ ├── embedding-*/ # Embedding adapters +│ ├── logger/ # @anvia/logger +│ ├── observability-*/ # Observability adapters +│ ├── provider-*/ # Provider adapters +│ ├── react/ # @anvia/react +│ ├── server/ # @anvia/server +│ ├── tool-*/ # Tool packages +│ └── vector-*/ # Vector store adapters └── bin/ # Local maintenance scripts ``` The workspace is declared in `pnpm-workspace.yaml` and includes: - `packages/*` -- `packages/*/*` - `apps/*` - `examples/*` diff --git a/README.md b/README.md index e3744242..5a7214ab 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ new Studio([agent]).start({ port: 4021 });

- Preview of Anvia Studio from packages/tools/studio. + Preview of Anvia Studio from packages/tool-studio.

## What You Can Build diff --git a/apps/docs/content/docs/changelog/anthropic.mdx b/apps/docs/content/docs/changelog/anthropic.mdx index 9f35afb9..705300b1 100644 --- a/apps/docs/content/docs/changelog/anthropic.mdx +++ b/apps/docs/content/docs/changelog/anthropic.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/anthropic." Anthropic provider adapter for Anvia. -Source: [`packages/providers/anthropic/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/anthropic/CHANGELOG.md) +Source: [`packages/provider-anthropic/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-anthropic/CHANGELOG.md) ## 0.3.5 diff --git a/apps/docs/content/docs/changelog/chroma.mdx b/apps/docs/content/docs/changelog/chroma.mdx index bafa18c0..00a18471 100644 --- a/apps/docs/content/docs/changelog/chroma.mdx +++ b/apps/docs/content/docs/changelog/chroma.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/chroma." ChromaDB vector store adapter for Anvia. -Source: [`packages/vector-stores/chroma/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/chroma/CHANGELOG.md) +Source: [`packages/vector-chroma/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-chroma/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/content/docs/changelog/fastembed.mdx b/apps/docs/content/docs/changelog/fastembed.mdx index 2f52c02c..adf907c1 100644 --- a/apps/docs/content/docs/changelog/fastembed.mdx +++ b/apps/docs/content/docs/changelog/fastembed.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/fastembed." FastEmbed embedding model adapter for Anvia. -Source: [`packages/embeddings/fastembed/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/fastembed/CHANGELOG.md) +Source: [`packages/embedding-fastembed/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embedding-fastembed/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/content/docs/changelog/gemini.mdx b/apps/docs/content/docs/changelog/gemini.mdx index 9ca6fbb4..b7cc4b10 100644 --- a/apps/docs/content/docs/changelog/gemini.mdx +++ b/apps/docs/content/docs/changelog/gemini.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/gemini." Gemini provider adapter for Anvia. -Source: [`packages/providers/gemini/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/gemini/CHANGELOG.md) +Source: [`packages/provider-gemini/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-gemini/CHANGELOG.md) ## 0.2.4 diff --git a/apps/docs/content/docs/changelog/index.mdx b/apps/docs/content/docs/changelog/index.mdx index efe513bf..2a5f9078 100644 --- a/apps/docs/content/docs/changelog/index.mdx +++ b/apps/docs/content/docs/changelog/index.mdx @@ -58,7 +58,7 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/sandbox` | `0.2.2` | [View changelog](/docs/changelog/sandbox) | +| `@anvia/sandbox` | `0.3.0` | [View changelog](/docs/changelog/sandbox) | | `@anvia/studio` | `0.5.12` | [View changelog](/docs/changelog/studio) | ## React diff --git a/apps/docs/content/docs/changelog/langfuse.mdx b/apps/docs/content/docs/changelog/langfuse.mdx index 4dcfb857..529a968b 100644 --- a/apps/docs/content/docs/changelog/langfuse.mdx +++ b/apps/docs/content/docs/changelog/langfuse.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/langfuse." Langfuse tracing adapter for Anvia. -Source: [`packages/observability/langfuse/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability/langfuse/CHANGELOG.md) +Source: [`packages/observability-langfuse/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability-langfuse/CHANGELOG.md) ## 0.2.3 diff --git a/apps/docs/content/docs/changelog/milvus.mdx b/apps/docs/content/docs/changelog/milvus.mdx index a66574b6..54b301ec 100644 --- a/apps/docs/content/docs/changelog/milvus.mdx +++ b/apps/docs/content/docs/changelog/milvus.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/milvus." Milvus vector store adapter for Anvia. -Source: [`packages/vector-stores/milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/milvus/CHANGELOG.md) +Source: [`packages/vector-milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-milvus/CHANGELOG.md) ## 0.3.0 diff --git a/apps/docs/content/docs/changelog/mistral.mdx b/apps/docs/content/docs/changelog/mistral.mdx index b78015ff..4862032a 100644 --- a/apps/docs/content/docs/changelog/mistral.mdx +++ b/apps/docs/content/docs/changelog/mistral.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/mistral." Mistral provider adapter for Anvia. -Source: [`packages/providers/mistral/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/mistral/CHANGELOG.md) +Source: [`packages/provider-mistral/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-mistral/CHANGELOG.md) ## 0.2.6 diff --git a/apps/docs/content/docs/changelog/openai.mdx b/apps/docs/content/docs/changelog/openai.mdx index 22e2c0fc..4c1324c5 100644 --- a/apps/docs/content/docs/changelog/openai.mdx +++ b/apps/docs/content/docs/changelog/openai.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/openai." OpenAI provider adapter for Anvia. -Source: [`packages/providers/openai/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/openai/CHANGELOG.md) +Source: [`packages/provider-openai/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-openai/CHANGELOG.md) ## 0.3.7 diff --git a/apps/docs/content/docs/changelog/otel.mdx b/apps/docs/content/docs/changelog/otel.mdx index b96975b9..d7be97cb 100644 --- a/apps/docs/content/docs/changelog/otel.mdx +++ b/apps/docs/content/docs/changelog/otel.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/otel." OpenTelemetry tracing adapter for Anvia. -Source: [`packages/observability/otel/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability/otel/CHANGELOG.md) +Source: [`packages/observability-otel/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability-otel/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/content/docs/changelog/pgvector.mdx b/apps/docs/content/docs/changelog/pgvector.mdx index 06f2ba6f..119b1f23 100644 --- a/apps/docs/content/docs/changelog/pgvector.mdx +++ b/apps/docs/content/docs/changelog/pgvector.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/pgvector." Postgres pgvector store adapter for Anvia. -Source: [`packages/vector-stores/pgvector/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pgvector/CHANGELOG.md) +Source: [`packages/vector-pgvector/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-pgvector/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/content/docs/changelog/pinecone.mdx b/apps/docs/content/docs/changelog/pinecone.mdx index 717f6e91..a2792ae3 100644 --- a/apps/docs/content/docs/changelog/pinecone.mdx +++ b/apps/docs/content/docs/changelog/pinecone.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/pinecone." Pinecone vector store adapter for Anvia. -Source: [`packages/vector-stores/pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pinecone/CHANGELOG.md) +Source: [`packages/vector-pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-pinecone/CHANGELOG.md) ## 0.3.0 diff --git a/apps/docs/content/docs/changelog/qdrant.mdx b/apps/docs/content/docs/changelog/qdrant.mdx index 8cc189dd..402b2cbe 100644 --- a/apps/docs/content/docs/changelog/qdrant.mdx +++ b/apps/docs/content/docs/changelog/qdrant.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/qdrant." Qdrant vector store adapter for Anvia. -Source: [`packages/vector-stores/qdrant/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/qdrant/CHANGELOG.md) +Source: [`packages/vector-qdrant/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-qdrant/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/content/docs/changelog/sandbox.mdx b/apps/docs/content/docs/changelog/sandbox.mdx index a39dbd51..ca629d84 100644 --- a/apps/docs/content/docs/changelog/sandbox.mdx +++ b/apps/docs/content/docs/changelog/sandbox.mdx @@ -7,7 +7,13 @@ description: "Release notes for @anvia/sandbox." Sandboxed workspace tools for Anvia agents. -Source: [`packages/tools/sandbox/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/sandbox/CHANGELOG.md) +Source: [`packages/tool-sandbox/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tool-sandbox/CHANGELOG.md) + +## 0.3.0 + +### Minor Changes + +- 18403b7: Add sandbox V2 controls for persistent workspaces, lifecycle cleanup, streaming command execution, file-size limits, observability hooks, language presets, and model-facing tool policies. ## 0.2.2 diff --git a/apps/docs/content/docs/changelog/studio.mdx b/apps/docs/content/docs/changelog/studio.mdx index a5efc0b3..7d43f520 100644 --- a/apps/docs/content/docs/changelog/studio.mdx +++ b/apps/docs/content/docs/changelog/studio.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/studio." Studio UI and HTTP runtime for Anvia agents. -Source: [`packages/tools/studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/studio/CHANGELOG.md) +Source: [`packages/tool-studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tool-studio/CHANGELOG.md) ## 0.5.12 diff --git a/apps/docs/content/docs/changelog/transformers.mdx b/apps/docs/content/docs/changelog/transformers.mdx index d0579ab2..c0407d10 100644 --- a/apps/docs/content/docs/changelog/transformers.mdx +++ b/apps/docs/content/docs/changelog/transformers.mdx @@ -7,7 +7,7 @@ description: "Release notes for @anvia/transformers." Transformers.js embedding model adapter for Anvia. -Source: [`packages/embeddings/transformers/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/transformers/CHANGELOG.md) +Source: [`packages/embedding-transformers/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embedding-transformers/CHANGELOG.md) ## 0.2.5 diff --git a/apps/docs/scripts/check-reference-coverage.mjs b/apps/docs/scripts/check-reference-coverage.mjs index b9ea08da..77a4158e 100644 --- a/apps/docs/scripts/check-reference-coverage.mjs +++ b/apps/docs/scripts/check-reference-coverage.mjs @@ -37,21 +37,12 @@ function walk(dir) { function discoverPackages() { const packageDirs = []; - for (const workspaceDir of ["packages"]) { - const root = join(repoRoot, workspaceDir); - for (const name of readdirSync(root)) { - const firstLevel = join(root, name); - if (!statSync(firstLevel).isDirectory()) continue; - - const firstLevelPackage = join(firstLevel, "package.json"); - if (existsSync(firstLevelPackage)) packageDirs.push(firstLevel); - - for (const childName of readdirSync(firstLevel)) { - const secondLevel = join(firstLevel, childName); - if (statSync(secondLevel).isDirectory() && existsSync(join(secondLevel, "package.json"))) { - packageDirs.push(secondLevel); - } - } + const root = join(repoRoot, "packages"); + + for (const name of readdirSync(root)) { + const dir = join(root, name); + if (statSync(dir).isDirectory() && existsSync(join(dir, "package.json"))) { + packageDirs.push(dir); } } diff --git a/apps/docs/scripts/generate-changelogs.mjs b/apps/docs/scripts/generate-changelogs.mjs index dce3e21e..ca557161 100644 --- a/apps/docs/scripts/generate-changelogs.mjs +++ b/apps/docs/scripts/generate-changelogs.mjs @@ -60,17 +60,27 @@ function discoverPackages() { return findPackageDirs(packagesRoot).map((dir) => { const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")); const pathParts = relative(packagesRoot, dir).split("/"); + const folderName = pathParts[0]; return { dir, pkg, - group: pathParts[0], + group: packageGroup(folderName), slug: packageSlug(pkg.name), changelogPath: join(dir, "CHANGELOG.md"), }; }); } +function packageGroup(folderName) { + if (folderName.startsWith("provider-")) return "providers"; + if (folderName.startsWith("embedding-")) return "embeddings"; + if (folderName.startsWith("vector-")) return "vector-stores"; + if (folderName.startsWith("observability-")) return "observability"; + if (folderName.startsWith("tool-")) return "tools"; + return folderName; +} + function findPackageDirs(dir) { const entries = readdirSync(dir).sort(); const dirs = []; diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts index 26a371fa..9080bbc1 100644 --- a/apps/docs/src/routeTree.gen.ts +++ b/apps/docs/src/routeTree.gen.ts @@ -208,3 +208,12 @@ const rootRouteChildren: RootRouteChildren = { export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/cookbook/tsconfig.json b/examples/cookbook/tsconfig.json index d831b559..05cebe60 100644 --- a/examples/cookbook/tsconfig.json +++ b/examples/cookbook/tsconfig.json @@ -7,23 +7,23 @@ "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "paths": { - "@anvia/chroma": ["../../packages/vector-stores/chroma/src/index.ts"], + "@anvia/chroma": ["../../packages/vector-chroma/src/index.ts"], "@anvia/core": ["../../packages/core/src/index.ts"], "@anvia/core/internal/agent": ["../../packages/core/src/internal/agent.ts"], "@anvia/core/*": ["../../packages/core/src/*/index.ts"], - "@anvia/fastembed": ["../../packages/embeddings/fastembed/src/index.ts"], - "@anvia/gemini": ["../../packages/providers/gemini/src/index.ts"], - "@anvia/langfuse": ["../../packages/observability/langfuse/src/index.ts"], + "@anvia/fastembed": ["../../packages/embedding-fastembed/src/index.ts"], + "@anvia/gemini": ["../../packages/provider-gemini/src/index.ts"], + "@anvia/langfuse": ["../../packages/observability-langfuse/src/index.ts"], "@anvia/logger": ["../../packages/logger/src/index.ts"], - "@anvia/mistral": ["../../packages/providers/mistral/src/index.ts"], - "@anvia/openai": ["../../packages/providers/openai/src/index.ts"], - "@anvia/otel": ["../../packages/observability/otel/src/index.ts"], - "@anvia/pgvector": ["../../packages/vector-stores/pgvector/src/index.ts"], - "@anvia/qdrant": ["../../packages/vector-stores/qdrant/src/index.ts"], + "@anvia/mistral": ["../../packages/provider-mistral/src/index.ts"], + "@anvia/openai": ["../../packages/provider-openai/src/index.ts"], + "@anvia/otel": ["../../packages/observability-otel/src/index.ts"], + "@anvia/pgvector": ["../../packages/vector-pgvector/src/index.ts"], + "@anvia/qdrant": ["../../packages/vector-qdrant/src/index.ts"], "@anvia/react": ["../../packages/react/src/index.ts"], "@anvia/server": ["../../packages/server/src/index.ts"], - "@anvia/studio": ["../../packages/tools/studio/src/index.ts"], - "@anvia/transformers": ["../../packages/embeddings/transformers/src/index.ts"] + "@anvia/studio": ["../../packages/tool-studio/src/index.ts"], + "@anvia/transformers": ["../../packages/embedding-transformers/src/index.ts"] } }, "include": [ diff --git a/package.json b/package.json index 7192d29a..367cea0a 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "changeset": "changeset", "github-releases": "node scripts/create-github-releases.mjs", "version-packages": "changeset version", - "release": "pnpm --filter @anvia/core build && pnpm --filter './packages/**' --filter '!@anvia/core' build && node scripts/publish-packages.mjs", + "release": "pnpm --filter @anvia/core build && pnpm --filter './packages/*' --filter '!@anvia/core' build && node scripts/publish-packages.mjs", "check": "biome check .", "check:staged": "biome check --staged --vcs-enabled=true --vcs-client-kind=git --vcs-use-ignore-file=true --no-errors-on-unmatched", "check:fix": "biome check --write .", diff --git a/packages/embeddings/fastembed/CHANGELOG.md b/packages/embedding-fastembed/CHANGELOG.md similarity index 100% rename from packages/embeddings/fastembed/CHANGELOG.md rename to packages/embedding-fastembed/CHANGELOG.md diff --git a/packages/embeddings/fastembed/README.md b/packages/embedding-fastembed/README.md similarity index 100% rename from packages/embeddings/fastembed/README.md rename to packages/embedding-fastembed/README.md diff --git a/packages/embeddings/fastembed/package.json b/packages/embedding-fastembed/package.json similarity index 94% rename from packages/embeddings/fastembed/package.json rename to packages/embedding-fastembed/package.json index b2a8eade..ff5735c9 100644 --- a/packages/embeddings/fastembed/package.json +++ b/packages/embedding-fastembed/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/embeddings/fastembed" + "directory": "packages/embedding-fastembed" }, "files": [ "dist" diff --git a/packages/embeddings/fastembed/src/index.ts b/packages/embedding-fastembed/src/index.ts similarity index 100% rename from packages/embeddings/fastembed/src/index.ts rename to packages/embedding-fastembed/src/index.ts diff --git a/packages/embeddings/fastembed/test/embedding.test.ts b/packages/embedding-fastembed/test/embedding.test.ts similarity index 100% rename from packages/embeddings/fastembed/test/embedding.test.ts rename to packages/embedding-fastembed/test/embedding.test.ts diff --git a/packages/embeddings/transformers/tsconfig.json b/packages/embedding-fastembed/tsconfig.json similarity index 69% rename from packages/embeddings/transformers/tsconfig.json rename to packages/embedding-fastembed/tsconfig.json index 9fd54c2d..6bb5dcd8 100644 --- a/packages/embeddings/transformers/tsconfig.json +++ b/packages/embedding-fastembed/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist" }, diff --git a/packages/embeddings/transformers/vitest.config.ts b/packages/embedding-fastembed/vitest.config.ts similarity index 50% rename from packages/embeddings/transformers/vitest.config.ts rename to packages/embedding-fastembed/vitest.config.ts index 8f0fea5e..cd35596b 100644 --- a/packages/embeddings/transformers/vitest.config.ts +++ b/packages/embedding-fastembed/vitest.config.ts @@ -3,9 +3,9 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ resolve: { alias: { - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, }, }, test: { diff --git a/packages/embeddings/transformers/CHANGELOG.md b/packages/embedding-transformers/CHANGELOG.md similarity index 100% rename from packages/embeddings/transformers/CHANGELOG.md rename to packages/embedding-transformers/CHANGELOG.md diff --git a/packages/embeddings/transformers/README.md b/packages/embedding-transformers/README.md similarity index 100% rename from packages/embeddings/transformers/README.md rename to packages/embedding-transformers/README.md diff --git a/packages/embeddings/transformers/package.json b/packages/embedding-transformers/package.json similarity index 94% rename from packages/embeddings/transformers/package.json rename to packages/embedding-transformers/package.json index 510c83c3..20177277 100644 --- a/packages/embeddings/transformers/package.json +++ b/packages/embedding-transformers/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/embeddings/transformers" + "directory": "packages/embedding-transformers" }, "files": [ "dist" diff --git a/packages/embeddings/transformers/src/index.ts b/packages/embedding-transformers/src/index.ts similarity index 100% rename from packages/embeddings/transformers/src/index.ts rename to packages/embedding-transformers/src/index.ts diff --git a/packages/embeddings/transformers/test/embedding.test.ts b/packages/embedding-transformers/test/embedding.test.ts similarity index 100% rename from packages/embeddings/transformers/test/embedding.test.ts rename to packages/embedding-transformers/test/embedding.test.ts diff --git a/packages/embeddings/fastembed/tsconfig.json b/packages/embedding-transformers/tsconfig.json similarity index 69% rename from packages/embeddings/fastembed/tsconfig.json rename to packages/embedding-transformers/tsconfig.json index 9fd54c2d..6bb5dcd8 100644 --- a/packages/embeddings/fastembed/tsconfig.json +++ b/packages/embedding-transformers/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist" }, diff --git a/packages/embeddings/fastembed/vitest.config.ts b/packages/embedding-transformers/vitest.config.ts similarity index 50% rename from packages/embeddings/fastembed/vitest.config.ts rename to packages/embedding-transformers/vitest.config.ts index 8f0fea5e..cd35596b 100644 --- a/packages/embeddings/fastembed/vitest.config.ts +++ b/packages/embedding-transformers/vitest.config.ts @@ -3,9 +3,9 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ resolve: { alias: { - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, }, }, test: { diff --git a/packages/observability/langfuse/CHANGELOG.md b/packages/observability-langfuse/CHANGELOG.md similarity index 100% rename from packages/observability/langfuse/CHANGELOG.md rename to packages/observability-langfuse/CHANGELOG.md diff --git a/packages/observability/langfuse/README.md b/packages/observability-langfuse/README.md similarity index 100% rename from packages/observability/langfuse/README.md rename to packages/observability-langfuse/README.md diff --git a/packages/observability/langfuse/package.json b/packages/observability-langfuse/package.json similarity index 95% rename from packages/observability/langfuse/package.json rename to packages/observability-langfuse/package.json index 1612ca7f..ea3b246f 100644 --- a/packages/observability/langfuse/package.json +++ b/packages/observability-langfuse/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/observability/langfuse" + "directory": "packages/observability-langfuse" }, "files": [ "dist" diff --git a/packages/observability/langfuse/src/index.ts b/packages/observability-langfuse/src/index.ts similarity index 100% rename from packages/observability/langfuse/src/index.ts rename to packages/observability-langfuse/src/index.ts diff --git a/packages/observability/langfuse/test/langfuse.test.ts b/packages/observability-langfuse/test/langfuse.test.ts similarity index 100% rename from packages/observability/langfuse/test/langfuse.test.ts rename to packages/observability-langfuse/test/langfuse.test.ts diff --git a/packages/observability/langfuse/tsconfig.json b/packages/observability-langfuse/tsconfig.json similarity index 69% rename from packages/observability/langfuse/tsconfig.json rename to packages/observability-langfuse/tsconfig.json index 9fd54c2d..6bb5dcd8 100644 --- a/packages/observability/langfuse/tsconfig.json +++ b/packages/observability-langfuse/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist" }, diff --git a/packages/observability-langfuse/vitest.config.ts b/packages/observability-langfuse/vitest.config.ts new file mode 100644 index 00000000..e362d69c --- /dev/null +++ b/packages/observability-langfuse/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/evals": new URL("../core/src/evals/index.ts", import.meta.url).pathname, + "@anvia/core/observability": new URL("../core/src/observability/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/observability/otel/CHANGELOG.md b/packages/observability-otel/CHANGELOG.md similarity index 100% rename from packages/observability/otel/CHANGELOG.md rename to packages/observability-otel/CHANGELOG.md diff --git a/packages/observability/otel/README.md b/packages/observability-otel/README.md similarity index 100% rename from packages/observability/otel/README.md rename to packages/observability-otel/README.md diff --git a/packages/observability/otel/package.json b/packages/observability-otel/package.json similarity index 95% rename from packages/observability/otel/package.json rename to packages/observability-otel/package.json index 912ed008..b1f1445f 100644 --- a/packages/observability/otel/package.json +++ b/packages/observability-otel/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/observability/otel" + "directory": "packages/observability-otel" }, "files": [ "dist" diff --git a/packages/observability/otel/src/index.ts b/packages/observability-otel/src/index.ts similarity index 100% rename from packages/observability/otel/src/index.ts rename to packages/observability-otel/src/index.ts diff --git a/packages/observability/otel/test/otel.test.ts b/packages/observability-otel/test/otel.test.ts similarity index 100% rename from packages/observability/otel/test/otel.test.ts rename to packages/observability-otel/test/otel.test.ts diff --git a/packages/observability/otel/tsconfig.json b/packages/observability-otel/tsconfig.json similarity index 69% rename from packages/observability/otel/tsconfig.json rename to packages/observability-otel/tsconfig.json index 9fd54c2d..6bb5dcd8 100644 --- a/packages/observability/otel/tsconfig.json +++ b/packages/observability-otel/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist" }, diff --git a/packages/observability-otel/vitest.config.ts b/packages/observability-otel/vitest.config.ts new file mode 100644 index 00000000..a2b56dd5 --- /dev/null +++ b/packages/observability-otel/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/observability": new URL("../core/src/observability/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/observability/langfuse/vitest.config.ts b/packages/observability/langfuse/vitest.config.ts deleted file mode 100644 index 93d7b2ea..00000000 --- a/packages/observability/langfuse/vitest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/evals": new URL("../../core/src/evals/index.ts", import.meta.url).pathname, - "@anvia/core/observability": new URL("../../core/src/observability/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/observability/otel/vitest.config.ts b/packages/observability/otel/vitest.config.ts deleted file mode 100644 index 89eb7921..00000000 --- a/packages/observability/otel/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/observability": new URL("../../core/src/observability/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/providers/anthropic/CHANGELOG.md b/packages/provider-anthropic/CHANGELOG.md similarity index 100% rename from packages/providers/anthropic/CHANGELOG.md rename to packages/provider-anthropic/CHANGELOG.md diff --git a/packages/providers/anthropic/README.md b/packages/provider-anthropic/README.md similarity index 100% rename from packages/providers/anthropic/README.md rename to packages/provider-anthropic/README.md diff --git a/packages/providers/anthropic/package.json b/packages/provider-anthropic/package.json similarity index 79% rename from packages/providers/anthropic/package.json rename to packages/provider-anthropic/package.json index 53e4b941..c9e183c2 100644 --- a/packages/providers/anthropic/package.json +++ b/packages/provider-anthropic/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/providers/anthropic" + "directory": "packages/provider-anthropic" }, "files": [ "dist" @@ -26,10 +26,10 @@ } }, "scripts": { - "prebuild": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "prebuild": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", "test": "vitest run", - "pretypecheck": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "pretypecheck": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/providers/anthropic/src/anthropic/client.ts b/packages/provider-anthropic/src/anthropic/client.ts similarity index 100% rename from packages/providers/anthropic/src/anthropic/client.ts rename to packages/provider-anthropic/src/anthropic/client.ts diff --git a/packages/providers/anthropic/src/anthropic/completion.ts b/packages/provider-anthropic/src/anthropic/completion.ts similarity index 100% rename from packages/providers/anthropic/src/anthropic/completion.ts rename to packages/provider-anthropic/src/anthropic/completion.ts diff --git a/packages/providers/anthropic/src/anthropic/index.ts b/packages/provider-anthropic/src/anthropic/index.ts similarity index 100% rename from packages/providers/anthropic/src/anthropic/index.ts rename to packages/provider-anthropic/src/anthropic/index.ts diff --git a/packages/providers/anthropic/src/index.ts b/packages/provider-anthropic/src/index.ts similarity index 100% rename from packages/providers/anthropic/src/index.ts rename to packages/provider-anthropic/src/index.ts diff --git a/packages/providers/anthropic/src/request-messages.ts b/packages/provider-anthropic/src/request-messages.ts similarity index 100% rename from packages/providers/anthropic/src/request-messages.ts rename to packages/provider-anthropic/src/request-messages.ts diff --git a/packages/providers/anthropic/src/utils.ts b/packages/provider-anthropic/src/utils.ts similarity index 100% rename from packages/providers/anthropic/src/utils.ts rename to packages/provider-anthropic/src/utils.ts diff --git a/packages/providers/anthropic/test/anthropic-completion.test.ts b/packages/provider-anthropic/test/anthropic-completion.test.ts similarity index 100% rename from packages/providers/anthropic/test/anthropic-completion.test.ts rename to packages/provider-anthropic/test/anthropic-completion.test.ts diff --git a/packages/providers/anthropic/test/client.test.ts b/packages/provider-anthropic/test/client.test.ts similarity index 100% rename from packages/providers/anthropic/test/client.test.ts rename to packages/provider-anthropic/test/client.test.ts diff --git a/packages/provider-anthropic/tsconfig.json b/packages/provider-anthropic/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/provider-anthropic/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/provider-anthropic/vitest.config.ts b/packages/provider-anthropic/vitest.config.ts new file mode 100644 index 00000000..8b9340fe --- /dev/null +++ b/packages/provider-anthropic/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/agent": new URL("../core/src/agent/index.ts", import.meta.url).pathname, + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/model-listing": new URL("../core/src/model-listing/index.ts", import.meta.url) + .pathname, + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/providers/gemini/CHANGELOG.md b/packages/provider-gemini/CHANGELOG.md similarity index 100% rename from packages/providers/gemini/CHANGELOG.md rename to packages/provider-gemini/CHANGELOG.md diff --git a/packages/providers/gemini/README.md b/packages/provider-gemini/README.md similarity index 100% rename from packages/providers/gemini/README.md rename to packages/provider-gemini/README.md diff --git a/packages/providers/gemini/package.json b/packages/provider-gemini/package.json similarity index 79% rename from packages/providers/gemini/package.json rename to packages/provider-gemini/package.json index 48b4b950..3ebb3c64 100644 --- a/packages/providers/gemini/package.json +++ b/packages/provider-gemini/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/providers/gemini" + "directory": "packages/provider-gemini" }, "files": [ "dist" @@ -26,10 +26,10 @@ } }, "scripts": { - "prebuild": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "prebuild": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", "test": "vitest run", - "pretypecheck": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "pretypecheck": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/providers/gemini/src/gemini/client.ts b/packages/provider-gemini/src/gemini/client.ts similarity index 100% rename from packages/providers/gemini/src/gemini/client.ts rename to packages/provider-gemini/src/gemini/client.ts diff --git a/packages/providers/gemini/src/gemini/completion.ts b/packages/provider-gemini/src/gemini/completion.ts similarity index 100% rename from packages/providers/gemini/src/gemini/completion.ts rename to packages/provider-gemini/src/gemini/completion.ts diff --git a/packages/providers/gemini/src/gemini/embedding.ts b/packages/provider-gemini/src/gemini/embedding.ts similarity index 100% rename from packages/providers/gemini/src/gemini/embedding.ts rename to packages/provider-gemini/src/gemini/embedding.ts diff --git a/packages/providers/gemini/src/gemini/image-generation.ts b/packages/provider-gemini/src/gemini/image-generation.ts similarity index 100% rename from packages/providers/gemini/src/gemini/image-generation.ts rename to packages/provider-gemini/src/gemini/image-generation.ts diff --git a/packages/providers/gemini/src/gemini/index.ts b/packages/provider-gemini/src/gemini/index.ts similarity index 100% rename from packages/providers/gemini/src/gemini/index.ts rename to packages/provider-gemini/src/gemini/index.ts diff --git a/packages/providers/gemini/src/gemini/transcription.ts b/packages/provider-gemini/src/gemini/transcription.ts similarity index 100% rename from packages/providers/gemini/src/gemini/transcription.ts rename to packages/provider-gemini/src/gemini/transcription.ts diff --git a/packages/providers/gemini/src/index.ts b/packages/provider-gemini/src/index.ts similarity index 100% rename from packages/providers/gemini/src/index.ts rename to packages/provider-gemini/src/index.ts diff --git a/packages/providers/gemini/src/request-messages.ts b/packages/provider-gemini/src/request-messages.ts similarity index 100% rename from packages/providers/gemini/src/request-messages.ts rename to packages/provider-gemini/src/request-messages.ts diff --git a/packages/providers/gemini/test/client.test.ts b/packages/provider-gemini/test/client.test.ts similarity index 100% rename from packages/providers/gemini/test/client.test.ts rename to packages/provider-gemini/test/client.test.ts diff --git a/packages/providers/gemini/test/completion.test.ts b/packages/provider-gemini/test/completion.test.ts similarity index 100% rename from packages/providers/gemini/test/completion.test.ts rename to packages/provider-gemini/test/completion.test.ts diff --git a/packages/providers/gemini/test/embedding.test.ts b/packages/provider-gemini/test/embedding.test.ts similarity index 100% rename from packages/providers/gemini/test/embedding.test.ts rename to packages/provider-gemini/test/embedding.test.ts diff --git a/packages/providers/gemini/test/multimodal.test.ts b/packages/provider-gemini/test/multimodal.test.ts similarity index 100% rename from packages/providers/gemini/test/multimodal.test.ts rename to packages/provider-gemini/test/multimodal.test.ts diff --git a/packages/provider-gemini/tsconfig.json b/packages/provider-gemini/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/provider-gemini/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/provider-gemini/vitest.config.ts b/packages/provider-gemini/vitest.config.ts new file mode 100644 index 00000000..53ce3386 --- /dev/null +++ b/packages/provider-gemini/vitest.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/audio-generation": new URL( + "../core/src/audio-generation/index.ts", + import.meta.url, + ).pathname, + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/image-generation": new URL( + "../core/src/image-generation/index.ts", + import.meta.url, + ).pathname, + "@anvia/core/model-listing": new URL("../core/src/model-listing/index.ts", import.meta.url) + .pathname, + "@anvia/core/transcription": new URL("../core/src/transcription/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/providers/mistral/CHANGELOG.md b/packages/provider-mistral/CHANGELOG.md similarity index 100% rename from packages/providers/mistral/CHANGELOG.md rename to packages/provider-mistral/CHANGELOG.md diff --git a/packages/providers/mistral/README.md b/packages/provider-mistral/README.md similarity index 100% rename from packages/providers/mistral/README.md rename to packages/provider-mistral/README.md diff --git a/packages/providers/mistral/package.json b/packages/provider-mistral/package.json similarity index 80% rename from packages/providers/mistral/package.json rename to packages/provider-mistral/package.json index a7bd298b..76012e4b 100644 --- a/packages/providers/mistral/package.json +++ b/packages/provider-mistral/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/providers/mistral" + "directory": "packages/provider-mistral" }, "files": [ "dist" @@ -26,10 +26,10 @@ } }, "scripts": { - "prebuild": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "prebuild": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", "test": "vitest run", - "pretypecheck": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "pretypecheck": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/providers/mistral/src/index.ts b/packages/provider-mistral/src/index.ts similarity index 100% rename from packages/providers/mistral/src/index.ts rename to packages/provider-mistral/src/index.ts diff --git a/packages/providers/mistral/src/mistral/client.ts b/packages/provider-mistral/src/mistral/client.ts similarity index 100% rename from packages/providers/mistral/src/mistral/client.ts rename to packages/provider-mistral/src/mistral/client.ts diff --git a/packages/providers/mistral/src/mistral/completion.ts b/packages/provider-mistral/src/mistral/completion.ts similarity index 100% rename from packages/providers/mistral/src/mistral/completion.ts rename to packages/provider-mistral/src/mistral/completion.ts diff --git a/packages/providers/mistral/src/mistral/embedding.ts b/packages/provider-mistral/src/mistral/embedding.ts similarity index 100% rename from packages/providers/mistral/src/mistral/embedding.ts rename to packages/provider-mistral/src/mistral/embedding.ts diff --git a/packages/providers/mistral/src/mistral/index.ts b/packages/provider-mistral/src/mistral/index.ts similarity index 100% rename from packages/providers/mistral/src/mistral/index.ts rename to packages/provider-mistral/src/mistral/index.ts diff --git a/packages/providers/mistral/src/request-messages.ts b/packages/provider-mistral/src/request-messages.ts similarity index 100% rename from packages/providers/mistral/src/request-messages.ts rename to packages/provider-mistral/src/request-messages.ts diff --git a/packages/providers/mistral/src/utils.ts b/packages/provider-mistral/src/utils.ts similarity index 100% rename from packages/providers/mistral/src/utils.ts rename to packages/provider-mistral/src/utils.ts diff --git a/packages/providers/mistral/test/client.test.ts b/packages/provider-mistral/test/client.test.ts similarity index 100% rename from packages/providers/mistral/test/client.test.ts rename to packages/provider-mistral/test/client.test.ts diff --git a/packages/providers/mistral/test/completion.test.ts b/packages/provider-mistral/test/completion.test.ts similarity index 100% rename from packages/providers/mistral/test/completion.test.ts rename to packages/provider-mistral/test/completion.test.ts diff --git a/packages/providers/mistral/test/embedding.test.ts b/packages/provider-mistral/test/embedding.test.ts similarity index 100% rename from packages/providers/mistral/test/embedding.test.ts rename to packages/provider-mistral/test/embedding.test.ts diff --git a/packages/provider-mistral/tsconfig.json b/packages/provider-mistral/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/provider-mistral/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/provider-mistral/vitest.config.ts b/packages/provider-mistral/vitest.config.ts new file mode 100644 index 00000000..aa1afec3 --- /dev/null +++ b/packages/provider-mistral/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/model-listing": new URL("../core/src/model-listing/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/providers/openai/CHANGELOG.md b/packages/provider-openai/CHANGELOG.md similarity index 100% rename from packages/providers/openai/CHANGELOG.md rename to packages/provider-openai/CHANGELOG.md diff --git a/packages/providers/openai/README.md b/packages/provider-openai/README.md similarity index 100% rename from packages/providers/openai/README.md rename to packages/provider-openai/README.md diff --git a/packages/providers/openai/package.json b/packages/provider-openai/package.json similarity index 79% rename from packages/providers/openai/package.json rename to packages/provider-openai/package.json index b28cffe0..dda33ca6 100644 --- a/packages/providers/openai/package.json +++ b/packages/provider-openai/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/providers/openai" + "directory": "packages/provider-openai" }, "files": [ "dist" @@ -26,9 +26,9 @@ } }, "scripts": { - "prebuild": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "prebuild": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", - "pretypecheck": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "pretypecheck": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "test": "vitest run", "typecheck": "tsc --noEmit" }, diff --git a/packages/providers/openai/src/index.ts b/packages/provider-openai/src/index.ts similarity index 100% rename from packages/providers/openai/src/index.ts rename to packages/provider-openai/src/index.ts diff --git a/packages/providers/openai/src/openai/audio-generation.ts b/packages/provider-openai/src/openai/audio-generation.ts similarity index 100% rename from packages/providers/openai/src/openai/audio-generation.ts rename to packages/provider-openai/src/openai/audio-generation.ts diff --git a/packages/providers/openai/src/openai/chat-completion.ts b/packages/provider-openai/src/openai/chat-completion.ts similarity index 100% rename from packages/providers/openai/src/openai/chat-completion.ts rename to packages/provider-openai/src/openai/chat-completion.ts diff --git a/packages/providers/openai/src/openai/client.ts b/packages/provider-openai/src/openai/client.ts similarity index 100% rename from packages/providers/openai/src/openai/client.ts rename to packages/provider-openai/src/openai/client.ts diff --git a/packages/providers/openai/src/openai/embedding.ts b/packages/provider-openai/src/openai/embedding.ts similarity index 100% rename from packages/providers/openai/src/openai/embedding.ts rename to packages/provider-openai/src/openai/embedding.ts diff --git a/packages/providers/openai/src/openai/image-generation.ts b/packages/provider-openai/src/openai/image-generation.ts similarity index 100% rename from packages/providers/openai/src/openai/image-generation.ts rename to packages/provider-openai/src/openai/image-generation.ts diff --git a/packages/providers/openai/src/openai/index.ts b/packages/provider-openai/src/openai/index.ts similarity index 100% rename from packages/providers/openai/src/openai/index.ts rename to packages/provider-openai/src/openai/index.ts diff --git a/packages/providers/openai/src/openai/responses.ts b/packages/provider-openai/src/openai/responses.ts similarity index 100% rename from packages/providers/openai/src/openai/responses.ts rename to packages/provider-openai/src/openai/responses.ts diff --git a/packages/providers/openai/src/openai/transcription.ts b/packages/provider-openai/src/openai/transcription.ts similarity index 100% rename from packages/providers/openai/src/openai/transcription.ts rename to packages/provider-openai/src/openai/transcription.ts diff --git a/packages/providers/openai/src/request-messages.ts b/packages/provider-openai/src/request-messages.ts similarity index 100% rename from packages/providers/openai/src/request-messages.ts rename to packages/provider-openai/src/request-messages.ts diff --git a/packages/providers/openai/src/utils.ts b/packages/provider-openai/src/utils.ts similarity index 100% rename from packages/providers/openai/src/utils.ts rename to packages/provider-openai/src/utils.ts diff --git a/packages/providers/openai/test/client.test.ts b/packages/provider-openai/test/client.test.ts similarity index 100% rename from packages/providers/openai/test/client.test.ts rename to packages/provider-openai/test/client.test.ts diff --git a/packages/providers/openai/test/embedding.test.ts b/packages/provider-openai/test/embedding.test.ts similarity index 100% rename from packages/providers/openai/test/embedding.test.ts rename to packages/provider-openai/test/embedding.test.ts diff --git a/packages/providers/openai/test/multimodal.test.ts b/packages/provider-openai/test/multimodal.test.ts similarity index 100% rename from packages/providers/openai/test/multimodal.test.ts rename to packages/provider-openai/test/multimodal.test.ts diff --git a/packages/providers/openai/test/openai-chat-completion.test.ts b/packages/provider-openai/test/openai-chat-completion.test.ts similarity index 100% rename from packages/providers/openai/test/openai-chat-completion.test.ts rename to packages/provider-openai/test/openai-chat-completion.test.ts diff --git a/packages/providers/openai/test/openai-responses.test.ts b/packages/provider-openai/test/openai-responses.test.ts similarity index 100% rename from packages/providers/openai/test/openai-responses.test.ts rename to packages/provider-openai/test/openai-responses.test.ts diff --git a/packages/provider-openai/tsconfig.json b/packages/provider-openai/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/provider-openai/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/provider-openai/vitest.config.ts b/packages/provider-openai/vitest.config.ts new file mode 100644 index 00000000..53ce3386 --- /dev/null +++ b/packages/provider-openai/vitest.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/audio-generation": new URL( + "../core/src/audio-generation/index.ts", + import.meta.url, + ).pathname, + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/image-generation": new URL( + "../core/src/image-generation/index.ts", + import.meta.url, + ).pathname, + "@anvia/core/model-listing": new URL("../core/src/model-listing/index.ts", import.meta.url) + .pathname, + "@anvia/core/transcription": new URL("../core/src/transcription/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/providers/anthropic/tsconfig.json b/packages/providers/anthropic/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/providers/anthropic/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/providers/anthropic/vitest.config.ts b/packages/providers/anthropic/vitest.config.ts deleted file mode 100644 index 279809c5..00000000 --- a/packages/providers/anthropic/vitest.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/agent": new URL("../../core/src/agent/index.ts", import.meta.url).pathname, - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/model-listing": new URL("../../core/src/model-listing/index.ts", import.meta.url) - .pathname, - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/providers/gemini/tsconfig.json b/packages/providers/gemini/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/providers/gemini/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/providers/gemini/vitest.config.ts b/packages/providers/gemini/vitest.config.ts deleted file mode 100644 index e56625c2..00000000 --- a/packages/providers/gemini/vitest.config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/audio-generation": new URL( - "../../core/src/audio-generation/index.ts", - import.meta.url, - ).pathname, - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/image-generation": new URL( - "../../core/src/image-generation/index.ts", - import.meta.url, - ).pathname, - "@anvia/core/model-listing": new URL("../../core/src/model-listing/index.ts", import.meta.url) - .pathname, - "@anvia/core/transcription": new URL("../../core/src/transcription/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/providers/mistral/tsconfig.json b/packages/providers/mistral/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/providers/mistral/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/providers/mistral/vitest.config.ts b/packages/providers/mistral/vitest.config.ts deleted file mode 100644 index 443710c4..00000000 --- a/packages/providers/mistral/vitest.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/model-listing": new URL("../../core/src/model-listing/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/providers/openai/tsconfig.json b/packages/providers/openai/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/providers/openai/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/providers/openai/vitest.config.ts b/packages/providers/openai/vitest.config.ts deleted file mode 100644 index e56625c2..00000000 --- a/packages/providers/openai/vitest.config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/audio-generation": new URL( - "../../core/src/audio-generation/index.ts", - import.meta.url, - ).pathname, - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/image-generation": new URL( - "../../core/src/image-generation/index.ts", - import.meta.url, - ).pathname, - "@anvia/core/model-listing": new URL("../../core/src/model-listing/index.ts", import.meta.url) - .pathname, - "@anvia/core/transcription": new URL("../../core/src/transcription/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/tools/sandbox/CHANGELOG.md b/packages/tool-sandbox/CHANGELOG.md similarity index 100% rename from packages/tools/sandbox/CHANGELOG.md rename to packages/tool-sandbox/CHANGELOG.md diff --git a/packages/tools/sandbox/package.json b/packages/tool-sandbox/package.json similarity index 87% rename from packages/tools/sandbox/package.json rename to packages/tool-sandbox/package.json index 487d9b13..088cd935 100644 --- a/packages/tools/sandbox/package.json +++ b/packages/tool-sandbox/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/tools/sandbox" + "directory": "packages/tool-sandbox" }, "files": [ "dist" @@ -27,7 +27,7 @@ }, "scripts": { "build": "pnpm run build:deps && tsup src/index.ts --format esm --dts --sourcemap --clean", - "build:deps": "test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build", + "build:deps": "test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build", "test": "pnpm run build:deps && vitest run", "typecheck": "pnpm run build:deps && tsc --noEmit" }, diff --git a/packages/tools/sandbox/src/docker-cli.ts b/packages/tool-sandbox/src/docker-cli.ts similarity index 100% rename from packages/tools/sandbox/src/docker-cli.ts rename to packages/tool-sandbox/src/docker-cli.ts diff --git a/packages/tools/sandbox/src/docker-sandbox.ts b/packages/tool-sandbox/src/docker-sandbox.ts similarity index 100% rename from packages/tools/sandbox/src/docker-sandbox.ts rename to packages/tool-sandbox/src/docker-sandbox.ts diff --git a/packages/tools/sandbox/src/errors.ts b/packages/tool-sandbox/src/errors.ts similarity index 100% rename from packages/tools/sandbox/src/errors.ts rename to packages/tool-sandbox/src/errors.ts diff --git a/packages/tools/sandbox/src/index.ts b/packages/tool-sandbox/src/index.ts similarity index 100% rename from packages/tools/sandbox/src/index.ts rename to packages/tool-sandbox/src/index.ts diff --git a/packages/tools/sandbox/src/path.ts b/packages/tool-sandbox/src/path.ts similarity index 100% rename from packages/tools/sandbox/src/path.ts rename to packages/tool-sandbox/src/path.ts diff --git a/packages/tools/sandbox/src/tools.ts b/packages/tool-sandbox/src/tools.ts similarity index 100% rename from packages/tools/sandbox/src/tools.ts rename to packages/tool-sandbox/src/tools.ts diff --git a/packages/tools/sandbox/src/types.ts b/packages/tool-sandbox/src/types.ts similarity index 100% rename from packages/tools/sandbox/src/types.ts rename to packages/tool-sandbox/src/types.ts diff --git a/packages/tools/sandbox/test/docker-sandbox.integration.test.ts b/packages/tool-sandbox/test/docker-sandbox.integration.test.ts similarity index 100% rename from packages/tools/sandbox/test/docker-sandbox.integration.test.ts rename to packages/tool-sandbox/test/docker-sandbox.integration.test.ts diff --git a/packages/tools/sandbox/test/path.test.ts b/packages/tool-sandbox/test/path.test.ts similarity index 100% rename from packages/tools/sandbox/test/path.test.ts rename to packages/tool-sandbox/test/path.test.ts diff --git a/packages/tools/sandbox/test/tools.test.ts b/packages/tool-sandbox/test/tools.test.ts similarity index 100% rename from packages/tools/sandbox/test/tools.test.ts rename to packages/tool-sandbox/test/tools.test.ts diff --git a/packages/tool-sandbox/tsconfig.json b/packages/tool-sandbox/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/tool-sandbox/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/tool-sandbox/vitest.config.ts b/packages/tool-sandbox/vitest.config.ts new file mode 100644 index 00000000..209e8cdf --- /dev/null +++ b/packages/tool-sandbox/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/tools/studio/CHANGELOG.md b/packages/tool-studio/CHANGELOG.md similarity index 100% rename from packages/tools/studio/CHANGELOG.md rename to packages/tool-studio/CHANGELOG.md diff --git a/packages/tools/studio/README.md b/packages/tool-studio/README.md similarity index 100% rename from packages/tools/studio/README.md rename to packages/tool-studio/README.md diff --git a/packages/tools/studio/components.json b/packages/tool-studio/components.json similarity index 100% rename from packages/tools/studio/components.json rename to packages/tool-studio/components.json diff --git a/packages/tools/studio/package.json b/packages/tool-studio/package.json similarity index 90% rename from packages/tools/studio/package.json rename to packages/tool-studio/package.json index d8c1db1e..d7fa085f 100644 --- a/packages/tools/studio/package.json +++ b/packages/tool-studio/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/tools/studio" + "directory": "packages/tool-studio" }, "files": [ "dist" @@ -27,7 +27,7 @@ }, "scripts": { "build": "pnpm run build:deps && tsup && vite build --config vite.config.ts", - "build:deps": "(test -f ../../core/dist/index.d.ts || pnpm --filter @anvia/core build) && pnpm --filter @anvia/server --filter @anvia/react build", + "build:deps": "(test -f ../core/dist/index.d.ts || pnpm --filter @anvia/core build) && pnpm --filter @anvia/server --filter @anvia/react build", "test": "pnpm run build:deps && vitest run", "typecheck": "pnpm run build:deps && tsc --noEmit" }, diff --git a/packages/tools/studio/src/index.ts b/packages/tool-studio/src/index.ts similarity index 100% rename from packages/tools/studio/src/index.ts rename to packages/tool-studio/src/index.ts diff --git a/packages/tools/studio/src/runner.ts b/packages/tool-studio/src/runner.ts similarity index 100% rename from packages/tools/studio/src/runner.ts rename to packages/tool-studio/src/runner.ts diff --git a/packages/tools/studio/src/runtime/approvals.ts b/packages/tool-studio/src/runtime/approvals.ts similarity index 100% rename from packages/tools/studio/src/runtime/approvals.ts rename to packages/tool-studio/src/runtime/approvals.ts diff --git a/packages/tools/studio/src/runtime/evals.ts b/packages/tool-studio/src/runtime/evals.ts similarity index 100% rename from packages/tools/studio/src/runtime/evals.ts rename to packages/tool-studio/src/runtime/evals.ts diff --git a/packages/tools/studio/src/runtime/json.ts b/packages/tool-studio/src/runtime/json.ts similarity index 100% rename from packages/tools/studio/src/runtime/json.ts rename to packages/tool-studio/src/runtime/json.ts diff --git a/packages/tools/studio/src/runtime/knowledge.ts b/packages/tool-studio/src/runtime/knowledge.ts similarity index 100% rename from packages/tools/studio/src/runtime/knowledge.ts rename to packages/tool-studio/src/runtime/knowledge.ts diff --git a/packages/tools/studio/src/runtime/mcps.ts b/packages/tool-studio/src/runtime/mcps.ts similarity index 100% rename from packages/tools/studio/src/runtime/mcps.ts rename to packages/tool-studio/src/runtime/mcps.ts diff --git a/packages/tools/studio/src/runtime/memory.ts b/packages/tool-studio/src/runtime/memory.ts similarity index 100% rename from packages/tools/studio/src/runtime/memory.ts rename to packages/tool-studio/src/runtime/memory.ts diff --git a/packages/tools/studio/src/runtime/observability.ts b/packages/tool-studio/src/runtime/observability.ts similarity index 100% rename from packages/tools/studio/src/runtime/observability.ts rename to packages/tool-studio/src/runtime/observability.ts diff --git a/packages/tools/studio/src/runtime/pipeline-logs.ts b/packages/tool-studio/src/runtime/pipeline-logs.ts similarity index 100% rename from packages/tools/studio/src/runtime/pipeline-logs.ts rename to packages/tool-studio/src/runtime/pipeline-logs.ts diff --git a/packages/tools/studio/src/runtime/pipelines.ts b/packages/tool-studio/src/runtime/pipelines.ts similarity index 100% rename from packages/tools/studio/src/runtime/pipelines.ts rename to packages/tool-studio/src/runtime/pipelines.ts diff --git a/packages/tools/studio/src/runtime/questions.ts b/packages/tool-studio/src/runtime/questions.ts similarity index 100% rename from packages/tools/studio/src/runtime/questions.ts rename to packages/tool-studio/src/runtime/questions.ts diff --git a/packages/tools/studio/src/runtime/runs.ts b/packages/tool-studio/src/runtime/runs.ts similarity index 100% rename from packages/tools/studio/src/runtime/runs.ts rename to packages/tool-studio/src/runtime/runs.ts diff --git a/packages/tools/studio/src/runtime/session-logs.ts b/packages/tool-studio/src/runtime/session-logs.ts similarity index 100% rename from packages/tools/studio/src/runtime/session-logs.ts rename to packages/tool-studio/src/runtime/session-logs.ts diff --git a/packages/tools/studio/src/runtime/sessions.ts b/packages/tool-studio/src/runtime/sessions.ts similarity index 100% rename from packages/tools/studio/src/runtime/sessions.ts rename to packages/tool-studio/src/runtime/sessions.ts diff --git a/packages/tools/studio/src/runtime/shared.ts b/packages/tool-studio/src/runtime/shared.ts similarity index 100% rename from packages/tools/studio/src/runtime/shared.ts rename to packages/tool-studio/src/runtime/shared.ts diff --git a/packages/tools/studio/src/runtime/status.ts b/packages/tool-studio/src/runtime/status.ts similarity index 100% rename from packages/tools/studio/src/runtime/status.ts rename to packages/tool-studio/src/runtime/status.ts diff --git a/packages/tools/studio/src/runtime/streams.ts b/packages/tool-studio/src/runtime/streams.ts similarity index 100% rename from packages/tools/studio/src/runtime/streams.ts rename to packages/tool-studio/src/runtime/streams.ts diff --git a/packages/tools/studio/src/runtime/studio.ts b/packages/tool-studio/src/runtime/studio.ts similarity index 100% rename from packages/tools/studio/src/runtime/studio.ts rename to packages/tool-studio/src/runtime/studio.ts diff --git a/packages/tools/studio/src/runtime/tool-metadata.ts b/packages/tool-studio/src/runtime/tool-metadata.ts similarity index 100% rename from packages/tools/studio/src/runtime/tool-metadata.ts rename to packages/tool-studio/src/runtime/tool-metadata.ts diff --git a/packages/tools/studio/src/runtime/tools.ts b/packages/tool-studio/src/runtime/tools.ts similarity index 100% rename from packages/tools/studio/src/runtime/tools.ts rename to packages/tool-studio/src/runtime/tools.ts diff --git a/packages/tools/studio/src/runtime/trace-routes.ts b/packages/tool-studio/src/runtime/trace-routes.ts similarity index 100% rename from packages/tools/studio/src/runtime/trace-routes.ts rename to packages/tool-studio/src/runtime/trace-routes.ts diff --git a/packages/tools/studio/src/runtime/transcript.ts b/packages/tool-studio/src/runtime/transcript.ts similarity index 100% rename from packages/tools/studio/src/runtime/transcript.ts rename to packages/tool-studio/src/runtime/transcript.ts diff --git a/packages/tools/studio/src/sqlite.ts b/packages/tool-studio/src/sqlite.ts similarity index 100% rename from packages/tools/studio/src/sqlite.ts rename to packages/tool-studio/src/sqlite.ts diff --git a/packages/tools/studio/src/storage/memory-store.ts b/packages/tool-studio/src/storage/memory-store.ts similarity index 100% rename from packages/tools/studio/src/storage/memory-store.ts rename to packages/tool-studio/src/storage/memory-store.ts diff --git a/packages/tools/studio/src/storage/sqlite-store.ts b/packages/tool-studio/src/storage/sqlite-store.ts similarity index 100% rename from packages/tools/studio/src/storage/sqlite-store.ts rename to packages/tool-studio/src/storage/sqlite-store.ts diff --git a/packages/tools/studio/src/trace.ts b/packages/tool-studio/src/trace.ts similarity index 100% rename from packages/tools/studio/src/trace.ts rename to packages/tool-studio/src/trace.ts diff --git a/packages/tools/studio/src/traces/trace-observer.ts b/packages/tool-studio/src/traces/trace-observer.ts similarity index 100% rename from packages/tools/studio/src/traces/trace-observer.ts rename to packages/tool-studio/src/traces/trace-observer.ts diff --git a/packages/tools/studio/src/types.ts b/packages/tool-studio/src/types.ts similarity index 100% rename from packages/tools/studio/src/types.ts rename to packages/tool-studio/src/types.ts diff --git a/packages/tools/studio/src/ui/app/app.tsx b/packages/tool-studio/src/ui/app/app.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/app.tsx rename to packages/tool-studio/src/ui/app/app.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/badge.tsx b/packages/tool-studio/src/ui/app/components/ui/badge.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/badge.tsx rename to packages/tool-studio/src/ui/app/components/ui/badge.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/button.tsx b/packages/tool-studio/src/ui/app/components/ui/button.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/button.tsx rename to packages/tool-studio/src/ui/app/components/ui/button.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/card.tsx b/packages/tool-studio/src/ui/app/components/ui/card.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/card.tsx rename to packages/tool-studio/src/ui/app/components/ui/card.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/dialog.tsx b/packages/tool-studio/src/ui/app/components/ui/dialog.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/dialog.tsx rename to packages/tool-studio/src/ui/app/components/ui/dialog.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/input.tsx b/packages/tool-studio/src/ui/app/components/ui/input.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/input.tsx rename to packages/tool-studio/src/ui/app/components/ui/input.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/scroll-area.tsx b/packages/tool-studio/src/ui/app/components/ui/scroll-area.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/scroll-area.tsx rename to packages/tool-studio/src/ui/app/components/ui/scroll-area.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/select.tsx b/packages/tool-studio/src/ui/app/components/ui/select.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/select.tsx rename to packages/tool-studio/src/ui/app/components/ui/select.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/separator.tsx b/packages/tool-studio/src/ui/app/components/ui/separator.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/separator.tsx rename to packages/tool-studio/src/ui/app/components/ui/separator.tsx diff --git a/packages/tools/studio/src/ui/app/components/ui/textarea.tsx b/packages/tool-studio/src/ui/app/components/ui/textarea.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/components/ui/textarea.tsx rename to packages/tool-studio/src/ui/app/components/ui/textarea.tsx diff --git a/packages/tools/studio/src/ui/app/index.html b/packages/tool-studio/src/ui/app/index.html similarity index 100% rename from packages/tools/studio/src/ui/app/index.html rename to packages/tool-studio/src/ui/app/index.html diff --git a/packages/tools/studio/src/ui/app/lib/utils.ts b/packages/tool-studio/src/ui/app/lib/utils.ts similarity index 100% rename from packages/tools/studio/src/ui/app/lib/utils.ts rename to packages/tool-studio/src/ui/app/lib/utils.ts diff --git a/packages/tools/studio/src/ui/app/main.tsx b/packages/tool-studio/src/ui/app/main.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/main.tsx rename to packages/tool-studio/src/ui/app/main.tsx diff --git a/packages/tools/studio/src/ui/app/modules/agents/agents-page.tsx b/packages/tool-studio/src/ui/app/modules/agents/agents-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/agents/agents-page.tsx rename to packages/tool-studio/src/ui/app/modules/agents/agents-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/evals/evals-page.tsx b/packages/tool-studio/src/ui/app/modules/evals/evals-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/evals/evals-page.tsx rename to packages/tool-studio/src/ui/app/modules/evals/evals-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/knowledge/knowledge-page.tsx b/packages/tool-studio/src/ui/app/modules/knowledge/knowledge-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/knowledge/knowledge-page.tsx rename to packages/tool-studio/src/ui/app/modules/knowledge/knowledge-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/mcps/mcps-page.tsx b/packages/tool-studio/src/ui/app/modules/mcps/mcps-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/mcps/mcps-page.tsx rename to packages/tool-studio/src/ui/app/modules/mcps/mcps-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/memory/memory-page.tsx b/packages/tool-studio/src/ui/app/modules/memory/memory-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/memory/memory-page.tsx rename to packages/tool-studio/src/ui/app/modules/memory/memory-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/pipelines/pipeline-logs-panel.tsx b/packages/tool-studio/src/ui/app/modules/pipelines/pipeline-logs-panel.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/pipelines/pipeline-logs-panel.tsx rename to packages/tool-studio/src/ui/app/modules/pipelines/pipeline-logs-panel.tsx diff --git a/packages/tools/studio/src/ui/app/modules/pipelines/pipelines-page.tsx b/packages/tool-studio/src/ui/app/modules/pipelines/pipelines-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/pipelines/pipelines-page.tsx rename to packages/tool-studio/src/ui/app/modules/pipelines/pipelines-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/playground/transcript-item.tsx b/packages/tool-studio/src/ui/app/modules/playground/transcript-item.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/playground/transcript-item.tsx rename to packages/tool-studio/src/ui/app/modules/playground/transcript-item.tsx diff --git a/packages/tools/studio/src/ui/app/modules/session-logs/session-logs-panel.tsx b/packages/tool-studio/src/ui/app/modules/session-logs/session-logs-panel.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/session-logs/session-logs-panel.tsx rename to packages/tool-studio/src/ui/app/modules/session-logs/session-logs-panel.tsx diff --git a/packages/tools/studio/src/ui/app/modules/sessions/sessions-page.tsx b/packages/tool-studio/src/ui/app/modules/sessions/sessions-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/sessions/sessions-page.tsx rename to packages/tool-studio/src/ui/app/modules/sessions/sessions-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/shared/format.ts b/packages/tool-studio/src/ui/app/modules/shared/format.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/format.ts rename to packages/tool-studio/src/ui/app/modules/shared/format.ts diff --git a/packages/tools/studio/src/ui/app/modules/shared/navigation.ts b/packages/tool-studio/src/ui/app/modules/shared/navigation.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/navigation.ts rename to packages/tool-studio/src/ui/app/modules/shared/navigation.ts diff --git a/packages/tools/studio/src/ui/app/modules/shared/object.ts b/packages/tool-studio/src/ui/app/modules/shared/object.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/object.ts rename to packages/tool-studio/src/ui/app/modules/shared/object.ts diff --git a/packages/tools/studio/src/ui/app/modules/shared/path.ts b/packages/tool-studio/src/ui/app/modules/shared/path.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/path.ts rename to packages/tool-studio/src/ui/app/modules/shared/path.ts diff --git a/packages/tools/studio/src/ui/app/modules/shared/renderers.tsx b/packages/tool-studio/src/ui/app/modules/shared/renderers.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/renderers.tsx rename to packages/tool-studio/src/ui/app/modules/shared/renderers.tsx diff --git a/packages/tools/studio/src/ui/app/modules/shared/transcript.ts b/packages/tool-studio/src/ui/app/modules/shared/transcript.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/transcript.ts rename to packages/tool-studio/src/ui/app/modules/shared/transcript.ts diff --git a/packages/tools/studio/src/ui/app/modules/shared/types.ts b/packages/tool-studio/src/ui/app/modules/shared/types.ts similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shared/types.ts rename to packages/tool-studio/src/ui/app/modules/shared/types.ts diff --git a/packages/tools/studio/src/ui/app/modules/shell/nav-button.tsx b/packages/tool-studio/src/ui/app/modules/shell/nav-button.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/shell/nav-button.tsx rename to packages/tool-studio/src/ui/app/modules/shell/nav-button.tsx diff --git a/packages/tools/studio/src/ui/app/modules/status/status-page.tsx b/packages/tool-studio/src/ui/app/modules/status/status-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/status/status-page.tsx rename to packages/tool-studio/src/ui/app/modules/status/status-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/tools/tools-page.tsx b/packages/tool-studio/src/ui/app/modules/tools/tools-page.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/tools/tools-page.tsx rename to packages/tool-studio/src/ui/app/modules/tools/tools-page.tsx diff --git a/packages/tools/studio/src/ui/app/modules/tracing/trace-browser.tsx b/packages/tool-studio/src/ui/app/modules/tracing/trace-browser.tsx similarity index 100% rename from packages/tools/studio/src/ui/app/modules/tracing/trace-browser.tsx rename to packages/tool-studio/src/ui/app/modules/tracing/trace-browser.tsx diff --git a/packages/tools/studio/src/ui/app/styles.css b/packages/tool-studio/src/ui/app/styles.css similarity index 100% rename from packages/tools/studio/src/ui/app/styles.css rename to packages/tool-studio/src/ui/app/styles.css diff --git a/packages/tools/studio/src/ui/assets/anvia.png b/packages/tool-studio/src/ui/assets/anvia.png similarity index 100% rename from packages/tools/studio/src/ui/assets/anvia.png rename to packages/tool-studio/src/ui/assets/anvia.png diff --git a/packages/tools/studio/src/ui/assets/logo.png b/packages/tool-studio/src/ui/assets/logo.png similarity index 100% rename from packages/tools/studio/src/ui/assets/logo.png rename to packages/tool-studio/src/ui/assets/logo.png diff --git a/packages/tools/studio/src/ui/public/assets/anvia.png b/packages/tool-studio/src/ui/public/assets/anvia.png similarity index 100% rename from packages/tools/studio/src/ui/public/assets/anvia.png rename to packages/tool-studio/src/ui/public/assets/anvia.png diff --git a/packages/tools/studio/src/ui/public/assets/logo.png b/packages/tool-studio/src/ui/public/assets/logo.png similarity index 100% rename from packages/tools/studio/src/ui/public/assets/logo.png rename to packages/tool-studio/src/ui/public/assets/logo.png diff --git a/packages/tools/studio/src/ui/routes.tsx b/packages/tool-studio/src/ui/routes.tsx similarity index 100% rename from packages/tools/studio/src/ui/routes.tsx rename to packages/tool-studio/src/ui/routes.tsx diff --git a/packages/tools/studio/test/runner.test.ts b/packages/tool-studio/test/runner.test.ts similarity index 100% rename from packages/tools/studio/test/runner.test.ts rename to packages/tool-studio/test/runner.test.ts diff --git a/packages/tools/studio/test/studio-ui-helpers.test.ts b/packages/tool-studio/test/studio-ui-helpers.test.ts similarity index 100% rename from packages/tools/studio/test/studio-ui-helpers.test.ts rename to packages/tool-studio/test/studio-ui-helpers.test.ts diff --git a/packages/tools/studio/test/trace-browser.test.ts b/packages/tool-studio/test/trace-browser.test.ts similarity index 100% rename from packages/tools/studio/test/trace-browser.test.ts rename to packages/tool-studio/test/trace-browser.test.ts diff --git a/packages/tools/studio/tsconfig.json b/packages/tool-studio/tsconfig.json similarity index 86% rename from packages/tools/studio/tsconfig.json rename to packages/tool-studio/tsconfig.json index d2ad5f19..a5ce09c4 100644 --- a/packages/tools/studio/tsconfig.json +++ b/packages/tool-studio/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", "jsx": "react-jsx", diff --git a/packages/tools/studio/tsup.config.ts b/packages/tool-studio/tsup.config.ts similarity index 100% rename from packages/tools/studio/tsup.config.ts rename to packages/tool-studio/tsup.config.ts diff --git a/packages/tools/studio/vite.config.ts b/packages/tool-studio/vite.config.ts similarity index 100% rename from packages/tools/studio/vite.config.ts rename to packages/tool-studio/vite.config.ts diff --git a/packages/tool-studio/vitest.config.ts b/packages/tool-studio/vitest.config.ts new file mode 100644 index 00000000..d82d3835 --- /dev/null +++ b/packages/tool-studio/vitest.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/agent": new URL("../core/src/agent/index.ts", import.meta.url).pathname, + "@anvia/core/completion": new URL("../core/src/completion/index.ts", import.meta.url) + .pathname, + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/evals": new URL("../core/src/evals/index.ts", import.meta.url).pathname, + "@anvia/core/internal/agent": new URL("../core/src/internal/agent.ts", import.meta.url) + .pathname, + "@anvia/core/mcp": new URL("../core/src/mcp/index.ts", import.meta.url).pathname, + "@anvia/core/memory": new URL("../core/src/memory/index.ts", import.meta.url).pathname, + "@anvia/core/observability": new URL("../core/src/observability/index.ts", import.meta.url) + .pathname, + "@anvia/core/pipeline": new URL("../core/src/pipeline/index.ts", import.meta.url).pathname, + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core/vector-store": new URL("../core/src/vector-store/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + "@": new URL("./src/ui/app", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/tools/sandbox/tsconfig.json b/packages/tools/sandbox/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/tools/sandbox/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/tools/sandbox/vitest.config.ts b/packages/tools/sandbox/vitest.config.ts deleted file mode 100644 index e53cf87b..00000000 --- a/packages/tools/sandbox/vitest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/tools/studio/vitest.config.ts b/packages/tools/studio/vitest.config.ts deleted file mode 100644 index 9d63d707..00000000 --- a/packages/tools/studio/vitest.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/agent": new URL("../../core/src/agent/index.ts", import.meta.url).pathname, - "@anvia/core/completion": new URL("../../core/src/completion/index.ts", import.meta.url) - .pathname, - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/evals": new URL("../../core/src/evals/index.ts", import.meta.url).pathname, - "@anvia/core/internal/agent": new URL("../../core/src/internal/agent.ts", import.meta.url) - .pathname, - "@anvia/core/mcp": new URL("../../core/src/mcp/index.ts", import.meta.url).pathname, - "@anvia/core/memory": new URL("../../core/src/memory/index.ts", import.meta.url).pathname, - "@anvia/core/observability": new URL("../../core/src/observability/index.ts", import.meta.url) - .pathname, - "@anvia/core/pipeline": new URL("../../core/src/pipeline/index.ts", import.meta.url).pathname, - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core/vector-store": new URL("../../core/src/vector-store/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - "@": new URL("./src/ui/app", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/vector-stores/chroma/CHANGELOG.md b/packages/vector-chroma/CHANGELOG.md similarity index 100% rename from packages/vector-stores/chroma/CHANGELOG.md rename to packages/vector-chroma/CHANGELOG.md diff --git a/packages/vector-stores/chroma/README.md b/packages/vector-chroma/README.md similarity index 100% rename from packages/vector-stores/chroma/README.md rename to packages/vector-chroma/README.md diff --git a/packages/vector-stores/chroma/package.json b/packages/vector-chroma/package.json similarity index 94% rename from packages/vector-stores/chroma/package.json rename to packages/vector-chroma/package.json index 69851034..8f5b6ff7 100644 --- a/packages/vector-stores/chroma/package.json +++ b/packages/vector-chroma/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/vector-stores/chroma" + "directory": "packages/vector-chroma" }, "files": [ "dist" diff --git a/packages/vector-stores/chroma/src/filters.ts b/packages/vector-chroma/src/filters.ts similarity index 100% rename from packages/vector-stores/chroma/src/filters.ts rename to packages/vector-chroma/src/filters.ts diff --git a/packages/vector-stores/chroma/src/helpers.ts b/packages/vector-chroma/src/helpers.ts similarity index 100% rename from packages/vector-stores/chroma/src/helpers.ts rename to packages/vector-chroma/src/helpers.ts diff --git a/packages/vector-stores/chroma/src/index.ts b/packages/vector-chroma/src/index.ts similarity index 100% rename from packages/vector-stores/chroma/src/index.ts rename to packages/vector-chroma/src/index.ts diff --git a/packages/vector-stores/chroma/src/search-index.ts b/packages/vector-chroma/src/search-index.ts similarity index 100% rename from packages/vector-stores/chroma/src/search-index.ts rename to packages/vector-chroma/src/search-index.ts diff --git a/packages/vector-stores/chroma/src/store.ts b/packages/vector-chroma/src/store.ts similarity index 100% rename from packages/vector-stores/chroma/src/store.ts rename to packages/vector-chroma/src/store.ts diff --git a/packages/vector-stores/chroma/src/types.ts b/packages/vector-chroma/src/types.ts similarity index 100% rename from packages/vector-stores/chroma/src/types.ts rename to packages/vector-chroma/src/types.ts diff --git a/packages/vector-stores/chroma/test/chroma-vector-store.test.ts b/packages/vector-chroma/test/chroma-vector-store.test.ts similarity index 100% rename from packages/vector-stores/chroma/test/chroma-vector-store.test.ts rename to packages/vector-chroma/test/chroma-vector-store.test.ts diff --git a/packages/vector-chroma/tsconfig.json b/packages/vector-chroma/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/vector-chroma/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-chroma/vitest.config.ts b/packages/vector-chroma/vitest.config.ts new file mode 100644 index 00000000..8d49b668 --- /dev/null +++ b/packages/vector-chroma/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core/vector-store": new URL("../core/src/vector-store/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/vector-stores/milvus/CHANGELOG.md b/packages/vector-milvus/CHANGELOG.md similarity index 100% rename from packages/vector-stores/milvus/CHANGELOG.md rename to packages/vector-milvus/CHANGELOG.md diff --git a/packages/vector-stores/milvus/package.json b/packages/vector-milvus/package.json similarity index 94% rename from packages/vector-stores/milvus/package.json rename to packages/vector-milvus/package.json index 136b2f60..7c7ba0e2 100644 --- a/packages/vector-stores/milvus/package.json +++ b/packages/vector-milvus/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/vector-stores/milvus" + "directory": "packages/vector-milvus" }, "files": [ "dist" diff --git a/packages/vector-stores/milvus/src/filters.ts b/packages/vector-milvus/src/filters.ts similarity index 100% rename from packages/vector-stores/milvus/src/filters.ts rename to packages/vector-milvus/src/filters.ts diff --git a/packages/vector-stores/milvus/src/helpers.ts b/packages/vector-milvus/src/helpers.ts similarity index 100% rename from packages/vector-stores/milvus/src/helpers.ts rename to packages/vector-milvus/src/helpers.ts diff --git a/packages/vector-stores/milvus/src/index.ts b/packages/vector-milvus/src/index.ts similarity index 100% rename from packages/vector-stores/milvus/src/index.ts rename to packages/vector-milvus/src/index.ts diff --git a/packages/vector-stores/milvus/src/search-index.ts b/packages/vector-milvus/src/search-index.ts similarity index 100% rename from packages/vector-stores/milvus/src/search-index.ts rename to packages/vector-milvus/src/search-index.ts diff --git a/packages/vector-stores/milvus/src/store.ts b/packages/vector-milvus/src/store.ts similarity index 100% rename from packages/vector-stores/milvus/src/store.ts rename to packages/vector-milvus/src/store.ts diff --git a/packages/vector-stores/milvus/src/types.ts b/packages/vector-milvus/src/types.ts similarity index 100% rename from packages/vector-stores/milvus/src/types.ts rename to packages/vector-milvus/src/types.ts diff --git a/packages/vector-stores/milvus/test/milvus-vector-store.test.ts b/packages/vector-milvus/test/milvus-vector-store.test.ts similarity index 100% rename from packages/vector-stores/milvus/test/milvus-vector-store.test.ts rename to packages/vector-milvus/test/milvus-vector-store.test.ts diff --git a/packages/vector-milvus/tsconfig.json b/packages/vector-milvus/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/vector-milvus/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-stores/pgvector/CHANGELOG.md b/packages/vector-pgvector/CHANGELOG.md similarity index 100% rename from packages/vector-stores/pgvector/CHANGELOG.md rename to packages/vector-pgvector/CHANGELOG.md diff --git a/packages/vector-stores/pgvector/README.md b/packages/vector-pgvector/README.md similarity index 100% rename from packages/vector-stores/pgvector/README.md rename to packages/vector-pgvector/README.md diff --git a/packages/vector-stores/pgvector/package.json b/packages/vector-pgvector/package.json similarity index 94% rename from packages/vector-stores/pgvector/package.json rename to packages/vector-pgvector/package.json index f8f6355c..18e742f5 100644 --- a/packages/vector-stores/pgvector/package.json +++ b/packages/vector-pgvector/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/vector-stores/pgvector" + "directory": "packages/vector-pgvector" }, "files": [ "dist" diff --git a/packages/vector-stores/pgvector/src/filters.ts b/packages/vector-pgvector/src/filters.ts similarity index 100% rename from packages/vector-stores/pgvector/src/filters.ts rename to packages/vector-pgvector/src/filters.ts diff --git a/packages/vector-stores/pgvector/src/helpers.ts b/packages/vector-pgvector/src/helpers.ts similarity index 100% rename from packages/vector-stores/pgvector/src/helpers.ts rename to packages/vector-pgvector/src/helpers.ts diff --git a/packages/vector-stores/pgvector/src/index.ts b/packages/vector-pgvector/src/index.ts similarity index 100% rename from packages/vector-stores/pgvector/src/index.ts rename to packages/vector-pgvector/src/index.ts diff --git a/packages/vector-stores/pgvector/src/search-index.ts b/packages/vector-pgvector/src/search-index.ts similarity index 100% rename from packages/vector-stores/pgvector/src/search-index.ts rename to packages/vector-pgvector/src/search-index.ts diff --git a/packages/vector-stores/pgvector/src/store.ts b/packages/vector-pgvector/src/store.ts similarity index 100% rename from packages/vector-stores/pgvector/src/store.ts rename to packages/vector-pgvector/src/store.ts diff --git a/packages/vector-stores/pgvector/src/types.ts b/packages/vector-pgvector/src/types.ts similarity index 100% rename from packages/vector-stores/pgvector/src/types.ts rename to packages/vector-pgvector/src/types.ts diff --git a/packages/vector-stores/pgvector/test/pgvector-store.test.ts b/packages/vector-pgvector/test/pgvector-store.test.ts similarity index 100% rename from packages/vector-stores/pgvector/test/pgvector-store.test.ts rename to packages/vector-pgvector/test/pgvector-store.test.ts diff --git a/packages/vector-pgvector/tsconfig.json b/packages/vector-pgvector/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/vector-pgvector/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-pgvector/vitest.config.ts b/packages/vector-pgvector/vitest.config.ts new file mode 100644 index 00000000..8d49b668 --- /dev/null +++ b/packages/vector-pgvector/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core/vector-store": new URL("../core/src/vector-store/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/vector-stores/pinecone/CHANGELOG.md b/packages/vector-pinecone/CHANGELOG.md similarity index 100% rename from packages/vector-stores/pinecone/CHANGELOG.md rename to packages/vector-pinecone/CHANGELOG.md diff --git a/packages/vector-stores/pinecone/package.json b/packages/vector-pinecone/package.json similarity index 94% rename from packages/vector-stores/pinecone/package.json rename to packages/vector-pinecone/package.json index 50a0a83f..ac753cf0 100644 --- a/packages/vector-stores/pinecone/package.json +++ b/packages/vector-pinecone/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/vector-stores/pinecone" + "directory": "packages/vector-pinecone" }, "files": [ "dist" diff --git a/packages/vector-stores/pinecone/src/filters.ts b/packages/vector-pinecone/src/filters.ts similarity index 100% rename from packages/vector-stores/pinecone/src/filters.ts rename to packages/vector-pinecone/src/filters.ts diff --git a/packages/vector-stores/pinecone/src/helpers.ts b/packages/vector-pinecone/src/helpers.ts similarity index 100% rename from packages/vector-stores/pinecone/src/helpers.ts rename to packages/vector-pinecone/src/helpers.ts diff --git a/packages/vector-stores/pinecone/src/index.ts b/packages/vector-pinecone/src/index.ts similarity index 100% rename from packages/vector-stores/pinecone/src/index.ts rename to packages/vector-pinecone/src/index.ts diff --git a/packages/vector-stores/pinecone/src/search-index.ts b/packages/vector-pinecone/src/search-index.ts similarity index 100% rename from packages/vector-stores/pinecone/src/search-index.ts rename to packages/vector-pinecone/src/search-index.ts diff --git a/packages/vector-stores/pinecone/src/store.ts b/packages/vector-pinecone/src/store.ts similarity index 100% rename from packages/vector-stores/pinecone/src/store.ts rename to packages/vector-pinecone/src/store.ts diff --git a/packages/vector-stores/pinecone/src/types.ts b/packages/vector-pinecone/src/types.ts similarity index 100% rename from packages/vector-stores/pinecone/src/types.ts rename to packages/vector-pinecone/src/types.ts diff --git a/packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts b/packages/vector-pinecone/test/pinecone-vector-store.test.ts similarity index 100% rename from packages/vector-stores/pinecone/test/pinecone-vector-store.test.ts rename to packages/vector-pinecone/test/pinecone-vector-store.test.ts diff --git a/packages/vector-pinecone/tsconfig.json b/packages/vector-pinecone/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/vector-pinecone/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-stores/qdrant/CHANGELOG.md b/packages/vector-qdrant/CHANGELOG.md similarity index 100% rename from packages/vector-stores/qdrant/CHANGELOG.md rename to packages/vector-qdrant/CHANGELOG.md diff --git a/packages/vector-stores/qdrant/README.md b/packages/vector-qdrant/README.md similarity index 100% rename from packages/vector-stores/qdrant/README.md rename to packages/vector-qdrant/README.md diff --git a/packages/vector-stores/qdrant/package.json b/packages/vector-qdrant/package.json similarity index 94% rename from packages/vector-stores/qdrant/package.json rename to packages/vector-qdrant/package.json index 1246b709..dc86d905 100644 --- a/packages/vector-stores/qdrant/package.json +++ b/packages/vector-qdrant/package.json @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/anvia-hq/anvia", - "directory": "packages/vector-stores/qdrant" + "directory": "packages/vector-qdrant" }, "files": [ "dist" diff --git a/packages/vector-stores/qdrant/src/filters.ts b/packages/vector-qdrant/src/filters.ts similarity index 100% rename from packages/vector-stores/qdrant/src/filters.ts rename to packages/vector-qdrant/src/filters.ts diff --git a/packages/vector-stores/qdrant/src/helpers.ts b/packages/vector-qdrant/src/helpers.ts similarity index 100% rename from packages/vector-stores/qdrant/src/helpers.ts rename to packages/vector-qdrant/src/helpers.ts diff --git a/packages/vector-stores/qdrant/src/index.ts b/packages/vector-qdrant/src/index.ts similarity index 100% rename from packages/vector-stores/qdrant/src/index.ts rename to packages/vector-qdrant/src/index.ts diff --git a/packages/vector-stores/qdrant/src/search-index.ts b/packages/vector-qdrant/src/search-index.ts similarity index 100% rename from packages/vector-stores/qdrant/src/search-index.ts rename to packages/vector-qdrant/src/search-index.ts diff --git a/packages/vector-stores/qdrant/src/store.ts b/packages/vector-qdrant/src/store.ts similarity index 100% rename from packages/vector-stores/qdrant/src/store.ts rename to packages/vector-qdrant/src/store.ts diff --git a/packages/vector-stores/qdrant/src/types.ts b/packages/vector-qdrant/src/types.ts similarity index 100% rename from packages/vector-stores/qdrant/src/types.ts rename to packages/vector-qdrant/src/types.ts diff --git a/packages/vector-stores/qdrant/test/qdrant-vector-store.test.ts b/packages/vector-qdrant/test/qdrant-vector-store.test.ts similarity index 100% rename from packages/vector-stores/qdrant/test/qdrant-vector-store.test.ts rename to packages/vector-qdrant/test/qdrant-vector-store.test.ts diff --git a/packages/vector-qdrant/tsconfig.json b/packages/vector-qdrant/tsconfig.json new file mode 100644 index 00000000..6bb5dcd8 --- /dev/null +++ b/packages/vector-qdrant/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-qdrant/vitest.config.ts b/packages/vector-qdrant/vitest.config.ts new file mode 100644 index 00000000..8d49b668 --- /dev/null +++ b/packages/vector-qdrant/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@anvia/core/embeddings": new URL("../core/src/embeddings/index.ts", import.meta.url) + .pathname, + "@anvia/core/tool": new URL("../core/src/tool/index.ts", import.meta.url).pathname, + "@anvia/core/vector-store": new URL("../core/src/vector-store/index.ts", import.meta.url) + .pathname, + "@anvia/core": new URL("../core/src/index.ts", import.meta.url).pathname, + }, + }, + test: { + environment: "node", + }, +}); diff --git a/packages/vector-stores/chroma/tsconfig.json b/packages/vector-stores/chroma/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/vector-stores/chroma/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/vector-stores/chroma/vitest.config.ts b/packages/vector-stores/chroma/vitest.config.ts deleted file mode 100644 index 77555fef..00000000 --- a/packages/vector-stores/chroma/vitest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core/vector-store": new URL("../../core/src/vector-store/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/vector-stores/milvus/tsconfig.json b/packages/vector-stores/milvus/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/vector-stores/milvus/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/vector-stores/pgvector/tsconfig.json b/packages/vector-stores/pgvector/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/vector-stores/pgvector/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/vector-stores/pgvector/vitest.config.ts b/packages/vector-stores/pgvector/vitest.config.ts deleted file mode 100644 index 77555fef..00000000 --- a/packages/vector-stores/pgvector/vitest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core/vector-store": new URL("../../core/src/vector-store/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/packages/vector-stores/pinecone/tsconfig.json b/packages/vector-stores/pinecone/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/vector-stores/pinecone/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/vector-stores/qdrant/tsconfig.json b/packages/vector-stores/qdrant/tsconfig.json deleted file mode 100644 index 9fd54c2d..00000000 --- a/packages/vector-stores/qdrant/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src", "test", "vitest.config.ts"] -} diff --git a/packages/vector-stores/qdrant/vitest.config.ts b/packages/vector-stores/qdrant/vitest.config.ts deleted file mode 100644 index 77555fef..00000000 --- a/packages/vector-stores/qdrant/vitest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - resolve: { - alias: { - "@anvia/core/embeddings": new URL("../../core/src/embeddings/index.ts", import.meta.url) - .pathname, - "@anvia/core/tool": new URL("../../core/src/tool/index.ts", import.meta.url).pathname, - "@anvia/core/vector-store": new URL("../../core/src/vector-store/index.ts", import.meta.url) - .pathname, - "@anvia/core": new URL("../../core/src/index.ts", import.meta.url).pathname, - }, - }, - test: { - environment: "node", - }, -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f8df2d0..55451267 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,7 +110,7 @@ importers: version: link:../../packages/core '@anvia/openai': specifier: workspace:* - version: link:../../packages/providers/openai + version: link:../../packages/provider-openai dotenv: specifier: ^17.4.2 version: 17.4.2 @@ -156,52 +156,52 @@ importers: dependencies: '@anvia/chroma': specifier: workspace:* - version: link:../../packages/vector-stores/chroma + version: link:../../packages/vector-chroma '@anvia/core': specifier: workspace:* version: link:../../packages/core '@anvia/fastembed': specifier: workspace:* - version: link:../../packages/embeddings/fastembed + version: link:../../packages/embedding-fastembed '@anvia/gemini': specifier: workspace:* - version: link:../../packages/providers/gemini + version: link:../../packages/provider-gemini '@anvia/langfuse': specifier: workspace:* - version: link:../../packages/observability/langfuse + version: link:../../packages/observability-langfuse '@anvia/logger': specifier: workspace:* version: link:../../packages/logger '@anvia/mistral': specifier: workspace:* - version: link:../../packages/providers/mistral + version: link:../../packages/provider-mistral '@anvia/openai': specifier: workspace:* - version: link:../../packages/providers/openai + version: link:../../packages/provider-openai '@anvia/otel': specifier: workspace:* - version: link:../../packages/observability/otel + version: link:../../packages/observability-otel '@anvia/pgvector': specifier: workspace:* - version: link:../../packages/vector-stores/pgvector + version: link:../../packages/vector-pgvector '@anvia/qdrant': specifier: workspace:* - version: link:../../packages/vector-stores/qdrant + version: link:../../packages/vector-qdrant '@anvia/react': specifier: workspace:* version: link:../../packages/react '@anvia/sandbox': specifier: workspace:* - version: link:../../packages/tools/sandbox + version: link:../../packages/tool-sandbox '@anvia/server': specifier: workspace:* version: link:../../packages/server '@anvia/studio': specifier: workspace:* - version: link:../../packages/tools/studio + version: link:../../packages/tool-studio '@anvia/transformers': specifier: workspace:* - version: link:../../packages/embeddings/transformers + version: link:../../packages/embedding-transformers '@modelcontextprotocol/sdk': specifier: ^1.29.0 version: 1.29.0(zod@4.4.3) @@ -259,11 +259,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/embeddings/fastembed: + packages/embedding-fastembed: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core fastembed: specifier: ^2.1.0 version: 2.1.0 @@ -281,11 +281,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/embeddings/transformers: + packages/embedding-transformers: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@huggingface/transformers': specifier: ^4.2.0 version: 4.2.0 @@ -325,11 +325,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/observability/langfuse: + packages/observability-langfuse: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@langfuse/otel': specifier: ^5.4.1 version: 5.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)) @@ -353,11 +353,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/observability/otel: + packages/observability-otel: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@opentelemetry/api': specifier: ^1.9.1 version: 1.9.1 @@ -375,14 +375,14 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/providers/anthropic: + packages/provider-anthropic: dependencies: '@anthropic-ai/sdk': specifier: ^0.104.1 version: 0.104.1(zod@4.4.3) '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core devDependencies: '@types/node': specifier: ^24.9.1 @@ -397,11 +397,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/providers/gemini: + packages/provider-gemini: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@google/genai': specifier: ^2.8.0 version: 2.8.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) @@ -419,11 +419,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/providers/mistral: + packages/provider-mistral: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@mistralai/mistralai': specifier: ^2.2.5 version: 2.2.5 @@ -441,11 +441,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/providers/openai: + packages/provider-openai: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core openai: specifier: ^6.42.0 version: 6.42.0(ws@8.21.0)(zod@4.4.3) @@ -517,11 +517,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/tools/sandbox: + packages/tool-sandbox: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core zod: specifier: ^4.4.3 version: 4.4.3 @@ -539,17 +539,17 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/tools/studio: + packages/tool-studio: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@anvia/react': specifier: workspace:* - version: link:../../react + version: link:../react '@anvia/server': specifier: workspace:* - version: link:../../server + version: link:../server '@hono/node-server': specifier: ^2.0.4 version: 2.0.4(hono@4.12.25) @@ -636,11 +636,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/vector-stores/chroma: + packages/vector-chroma: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core chromadb: specifier: ^3.4.3 version: 3.4.3 @@ -658,11 +658,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/vector-stores/milvus: + packages/vector-milvus: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@zilliz/milvus2-sdk-node': specifier: ^2.5.0 version: 2.6.17 @@ -680,11 +680,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/vector-stores/pgvector: + packages/vector-pgvector: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core pg: specifier: ^8.21.0 version: 8.21.0 @@ -708,11 +708,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/vector-stores/pinecone: + packages/vector-pinecone: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@pinecone-database/pinecone': specifier: ^5.1.0 version: 5.1.2 @@ -730,11 +730,11 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) - packages/vector-stores/qdrant: + packages/vector-qdrant: dependencies: '@anvia/core': specifier: workspace:* - version: link:../../core + version: link:../core '@qdrant/js-client-rest': specifier: ^1.18.0 version: 1.18.0(typescript@5.9.3) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 542fc63e..aa334edd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,5 @@ packages: - "packages/*" - - "packages/*/*" - "apps/*" - "examples/*" minimumReleaseAge: 10080 From f7a9cc882dc40d8f883a7fb5d2adb38636e2159f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:51:07 +0700 Subject: [PATCH 07/16] Version Packages (#108) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/flat-package-layout.md | 19 ------------------- packages/embedding-fastembed/CHANGELOG.md | 6 ++++++ packages/embedding-fastembed/package.json | 2 +- packages/embedding-transformers/CHANGELOG.md | 6 ++++++ packages/embedding-transformers/package.json | 2 +- packages/observability-langfuse/CHANGELOG.md | 6 ++++++ packages/observability-langfuse/package.json | 2 +- packages/observability-otel/CHANGELOG.md | 6 ++++++ packages/observability-otel/package.json | 2 +- packages/provider-anthropic/CHANGELOG.md | 6 ++++++ packages/provider-anthropic/package.json | 2 +- packages/provider-gemini/CHANGELOG.md | 6 ++++++ packages/provider-gemini/package.json | 2 +- packages/provider-mistral/CHANGELOG.md | 6 ++++++ packages/provider-mistral/package.json | 2 +- packages/provider-openai/CHANGELOG.md | 6 ++++++ packages/provider-openai/package.json | 2 +- packages/tool-sandbox/CHANGELOG.md | 6 ++++++ packages/tool-sandbox/package.json | 2 +- packages/tool-studio/CHANGELOG.md | 6 ++++++ packages/tool-studio/package.json | 2 +- packages/vector-chroma/CHANGELOG.md | 6 ++++++ packages/vector-chroma/package.json | 2 +- packages/vector-milvus/CHANGELOG.md | 6 ++++++ packages/vector-milvus/package.json | 2 +- packages/vector-pgvector/CHANGELOG.md | 6 ++++++ packages/vector-pgvector/package.json | 2 +- packages/vector-pinecone/CHANGELOG.md | 6 ++++++ packages/vector-pinecone/package.json | 2 +- packages/vector-qdrant/CHANGELOG.md | 6 ++++++ packages/vector-qdrant/package.json | 2 +- 31 files changed, 105 insertions(+), 34 deletions(-) delete mode 100644 .changeset/flat-package-layout.md diff --git a/.changeset/flat-package-layout.md b/.changeset/flat-package-layout.md deleted file mode 100644 index 5eaa1611..00000000 --- a/.changeset/flat-package-layout.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@anvia/anthropic": patch -"@anvia/chroma": patch -"@anvia/fastembed": patch -"@anvia/gemini": patch -"@anvia/langfuse": patch -"@anvia/milvus": patch -"@anvia/mistral": patch -"@anvia/openai": patch -"@anvia/otel": patch -"@anvia/pgvector": patch -"@anvia/pinecone": patch -"@anvia/qdrant": patch -"@anvia/sandbox": patch -"@anvia/studio": patch -"@anvia/transformers": patch ---- - -Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. diff --git a/packages/embedding-fastembed/CHANGELOG.md b/packages/embedding-fastembed/CHANGELOG.md index d6ef3fb0..020a832a 100644 --- a/packages/embedding-fastembed/CHANGELOG.md +++ b/packages/embedding-fastembed/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/fastembed +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/embedding-fastembed/package.json b/packages/embedding-fastembed/package.json index ff5735c9..0847a1aa 100644 --- a/packages/embedding-fastembed/package.json +++ b/packages/embedding-fastembed/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/fastembed", - "version": "0.2.5", + "version": "0.2.6", "description": "FastEmbed embedding model adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/embedding-transformers/CHANGELOG.md b/packages/embedding-transformers/CHANGELOG.md index 7dc16f35..2bc992e7 100644 --- a/packages/embedding-transformers/CHANGELOG.md +++ b/packages/embedding-transformers/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/transformers +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/embedding-transformers/package.json b/packages/embedding-transformers/package.json index 20177277..bd423ade 100644 --- a/packages/embedding-transformers/package.json +++ b/packages/embedding-transformers/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/transformers", - "version": "0.2.5", + "version": "0.2.6", "description": "Transformers.js embedding model adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/observability-langfuse/CHANGELOG.md b/packages/observability-langfuse/CHANGELOG.md index 2697c9e1..8acf5c64 100644 --- a/packages/observability-langfuse/CHANGELOG.md +++ b/packages/observability-langfuse/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/langfuse +## 0.2.4 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.3 ### Patch Changes diff --git a/packages/observability-langfuse/package.json b/packages/observability-langfuse/package.json index ea3b246f..db0dbd3c 100644 --- a/packages/observability-langfuse/package.json +++ b/packages/observability-langfuse/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/langfuse", - "version": "0.2.3", + "version": "0.2.4", "description": "Langfuse tracing adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/observability-otel/CHANGELOG.md b/packages/observability-otel/CHANGELOG.md index bfa46033..bcc336bd 100644 --- a/packages/observability-otel/CHANGELOG.md +++ b/packages/observability-otel/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/otel +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/observability-otel/package.json b/packages/observability-otel/package.json index b1f1445f..314a6564 100644 --- a/packages/observability-otel/package.json +++ b/packages/observability-otel/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/otel", - "version": "0.2.5", + "version": "0.2.6", "description": "OpenTelemetry tracing adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-anthropic/CHANGELOG.md b/packages/provider-anthropic/CHANGELOG.md index c6ab51b2..f0affd40 100644 --- a/packages/provider-anthropic/CHANGELOG.md +++ b/packages/provider-anthropic/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/anthropic +## 0.3.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.5 ### Patch Changes diff --git a/packages/provider-anthropic/package.json b/packages/provider-anthropic/package.json index c9e183c2..15bbd4c5 100644 --- a/packages/provider-anthropic/package.json +++ b/packages/provider-anthropic/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/anthropic", - "version": "0.3.5", + "version": "0.3.6", "description": "Anthropic provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-gemini/CHANGELOG.md b/packages/provider-gemini/CHANGELOG.md index fc1bb6ae..e66d5260 100644 --- a/packages/provider-gemini/CHANGELOG.md +++ b/packages/provider-gemini/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/gemini +## 0.2.5 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.4 ### Patch Changes diff --git a/packages/provider-gemini/package.json b/packages/provider-gemini/package.json index 3ebb3c64..4226693a 100644 --- a/packages/provider-gemini/package.json +++ b/packages/provider-gemini/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/gemini", - "version": "0.2.4", + "version": "0.2.5", "description": "Gemini provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-mistral/CHANGELOG.md b/packages/provider-mistral/CHANGELOG.md index 96f7e362..829c1249 100644 --- a/packages/provider-mistral/CHANGELOG.md +++ b/packages/provider-mistral/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/mistral +## 0.2.7 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.6 ### Patch Changes diff --git a/packages/provider-mistral/package.json b/packages/provider-mistral/package.json index 76012e4b..43d43d59 100644 --- a/packages/provider-mistral/package.json +++ b/packages/provider-mistral/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/mistral", - "version": "0.2.6", + "version": "0.2.7", "description": "Mistral provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-openai/CHANGELOG.md b/packages/provider-openai/CHANGELOG.md index 912b94c9..8302d9fd 100644 --- a/packages/provider-openai/CHANGELOG.md +++ b/packages/provider-openai/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/openai +## 0.3.8 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.7 ### Patch Changes diff --git a/packages/provider-openai/package.json b/packages/provider-openai/package.json index dda33ca6..225f9aa3 100644 --- a/packages/provider-openai/package.json +++ b/packages/provider-openai/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/openai", - "version": "0.3.7", + "version": "0.3.8", "description": "OpenAI provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/tool-sandbox/CHANGELOG.md b/packages/tool-sandbox/CHANGELOG.md index 5f694f7c..04392902 100644 --- a/packages/tool-sandbox/CHANGELOG.md +++ b/packages/tool-sandbox/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/sandbox +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/packages/tool-sandbox/package.json b/packages/tool-sandbox/package.json index 088cd935..2c0c917e 100644 --- a/packages/tool-sandbox/package.json +++ b/packages/tool-sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/sandbox", - "version": "0.3.0", + "version": "0.3.1", "description": "Sandboxed workspace tools for Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/tool-studio/CHANGELOG.md b/packages/tool-studio/CHANGELOG.md index 35544bcb..3fa34d3c 100644 --- a/packages/tool-studio/CHANGELOG.md +++ b/packages/tool-studio/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/studio +## 0.5.13 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.5.12 ### Patch Changes diff --git a/packages/tool-studio/package.json b/packages/tool-studio/package.json index d7fa085f..fd1c5256 100644 --- a/packages/tool-studio/package.json +++ b/packages/tool-studio/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/studio", - "version": "0.5.12", + "version": "0.5.13", "description": "Studio UI and HTTP runtime for Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-chroma/CHANGELOG.md b/packages/vector-chroma/CHANGELOG.md index 96b51729..80b37582 100644 --- a/packages/vector-chroma/CHANGELOG.md +++ b/packages/vector-chroma/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/chroma +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/vector-chroma/package.json b/packages/vector-chroma/package.json index 8f5b6ff7..731f7f5a 100644 --- a/packages/vector-chroma/package.json +++ b/packages/vector-chroma/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/chroma", - "version": "0.2.5", + "version": "0.2.6", "description": "ChromaDB vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-milvus/CHANGELOG.md b/packages/vector-milvus/CHANGELOG.md index 0a5a84cd..7103e5ad 100644 --- a/packages/vector-milvus/CHANGELOG.md +++ b/packages/vector-milvus/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/milvus +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/packages/vector-milvus/package.json b/packages/vector-milvus/package.json index 7c7ba0e2..d7faa8a8 100644 --- a/packages/vector-milvus/package.json +++ b/packages/vector-milvus/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/milvus", - "version": "0.3.0", + "version": "0.3.1", "description": "Milvus vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-pgvector/CHANGELOG.md b/packages/vector-pgvector/CHANGELOG.md index 2a5f470f..4e1c1321 100644 --- a/packages/vector-pgvector/CHANGELOG.md +++ b/packages/vector-pgvector/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/pgvector +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/vector-pgvector/package.json b/packages/vector-pgvector/package.json index 18e742f5..d1c41f93 100644 --- a/packages/vector-pgvector/package.json +++ b/packages/vector-pgvector/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/pgvector", - "version": "0.2.5", + "version": "0.2.6", "description": "Postgres pgvector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-pinecone/CHANGELOG.md b/packages/vector-pinecone/CHANGELOG.md index 0dcdd4f4..5151eb98 100644 --- a/packages/vector-pinecone/CHANGELOG.md +++ b/packages/vector-pinecone/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/pinecone +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/packages/vector-pinecone/package.json b/packages/vector-pinecone/package.json index ac753cf0..ff74c284 100644 --- a/packages/vector-pinecone/package.json +++ b/packages/vector-pinecone/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/pinecone", - "version": "0.3.0", + "version": "0.3.1", "description": "Pinecone vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-qdrant/CHANGELOG.md b/packages/vector-qdrant/CHANGELOG.md index 0b4dd929..cac29570 100644 --- a/packages/vector-qdrant/CHANGELOG.md +++ b/packages/vector-qdrant/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/qdrant +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/packages/vector-qdrant/package.json b/packages/vector-qdrant/package.json index dc86d905..c3543742 100644 --- a/packages/vector-qdrant/package.json +++ b/packages/vector-qdrant/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/qdrant", - "version": "0.2.5", + "version": "0.2.6", "description": "Qdrant vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", From 6c26cb6a9917d18398c7f98486f51b849dbc508d Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:25:40 +0700 Subject: [PATCH 08/16] docs: expand cookbook feature coverage (#109) --- .../docs/content/docs/changelog/anthropic.mdx | 6 ++ apps/docs/content/docs/changelog/chroma.mdx | 6 ++ .../docs/content/docs/changelog/fastembed.mdx | 6 ++ apps/docs/content/docs/changelog/gemini.mdx | 6 ++ apps/docs/content/docs/changelog/index.mdx | 30 ++++----- apps/docs/content/docs/changelog/langfuse.mdx | 6 ++ apps/docs/content/docs/changelog/milvus.mdx | 6 ++ apps/docs/content/docs/changelog/mistral.mdx | 6 ++ apps/docs/content/docs/changelog/openai.mdx | 6 ++ apps/docs/content/docs/changelog/otel.mdx | 6 ++ apps/docs/content/docs/changelog/pgvector.mdx | 6 ++ apps/docs/content/docs/changelog/pinecone.mdx | 6 ++ apps/docs/content/docs/changelog/qdrant.mdx | 6 ++ apps/docs/content/docs/changelog/sandbox.mdx | 6 ++ apps/docs/content/docs/changelog/studio.mdx | 6 ++ .../content/docs/changelog/transformers.mdx | 6 ++ apps/docs/content/docs/guides/cookbook.mdx | 22 +++++-- .../11-anthropic-text-call.ts | 19 ++++++ .../06_retrieval/12-milvus-vector-store.ts | 41 ++++++++++++ .../06_retrieval/13-pinecone-vector-store.ts | 55 ++++++++++++++++ .../10_integrations/07-console-logging.ts | 62 +++++++++++++++++++ examples/cookbook/README.md | 11 +++- examples/cookbook/compose.cookbook.yml | 27 ++++++++ examples/cookbook/package.json | 7 +++ examples/cookbook/tsconfig.json | 3 + package.json | 4 ++ pnpm-lock.yaml | 9 +++ 27 files changed, 357 insertions(+), 23 deletions(-) create mode 100644 examples/cookbook/04_providers_and_multimodal/11-anthropic-text-call.ts create mode 100644 examples/cookbook/06_retrieval/12-milvus-vector-store.ts create mode 100644 examples/cookbook/06_retrieval/13-pinecone-vector-store.ts create mode 100644 examples/cookbook/10_integrations/07-console-logging.ts diff --git a/apps/docs/content/docs/changelog/anthropic.mdx b/apps/docs/content/docs/changelog/anthropic.mdx index 705300b1..29b78e65 100644 --- a/apps/docs/content/docs/changelog/anthropic.mdx +++ b/apps/docs/content/docs/changelog/anthropic.mdx @@ -9,6 +9,12 @@ Anthropic provider adapter for Anvia. Source: [`packages/provider-anthropic/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-anthropic/CHANGELOG.md) +## 0.3.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/chroma.mdx b/apps/docs/content/docs/changelog/chroma.mdx index 00a18471..5b38cb5f 100644 --- a/apps/docs/content/docs/changelog/chroma.mdx +++ b/apps/docs/content/docs/changelog/chroma.mdx @@ -9,6 +9,12 @@ ChromaDB vector store adapter for Anvia. Source: [`packages/vector-chroma/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-chroma/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/fastembed.mdx b/apps/docs/content/docs/changelog/fastembed.mdx index adf907c1..ae2dc7c9 100644 --- a/apps/docs/content/docs/changelog/fastembed.mdx +++ b/apps/docs/content/docs/changelog/fastembed.mdx @@ -9,6 +9,12 @@ FastEmbed embedding model adapter for Anvia. Source: [`packages/embedding-fastembed/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embedding-fastembed/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/gemini.mdx b/apps/docs/content/docs/changelog/gemini.mdx index b7cc4b10..5dedbace 100644 --- a/apps/docs/content/docs/changelog/gemini.mdx +++ b/apps/docs/content/docs/changelog/gemini.mdx @@ -9,6 +9,12 @@ Gemini provider adapter for Anvia. Source: [`packages/provider-gemini/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-gemini/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/index.mdx b/apps/docs/content/docs/changelog/index.mdx index 2a5f9078..f30ac15e 100644 --- a/apps/docs/content/docs/changelog/index.mdx +++ b/apps/docs/content/docs/changelog/index.mdx @@ -19,27 +19,27 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/anthropic` | `0.3.5` | [View changelog](/docs/changelog/anthropic) | -| `@anvia/gemini` | `0.2.4` | [View changelog](/docs/changelog/gemini) | -| `@anvia/mistral` | `0.2.6` | [View changelog](/docs/changelog/mistral) | -| `@anvia/openai` | `0.3.7` | [View changelog](/docs/changelog/openai) | +| `@anvia/anthropic` | `0.3.6` | [View changelog](/docs/changelog/anthropic) | +| `@anvia/gemini` | `0.2.5` | [View changelog](/docs/changelog/gemini) | +| `@anvia/mistral` | `0.2.7` | [View changelog](/docs/changelog/mistral) | +| `@anvia/openai` | `0.3.8` | [View changelog](/docs/changelog/openai) | ## Embeddings | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/fastembed` | `0.2.5` | [View changelog](/docs/changelog/fastembed) | -| `@anvia/transformers` | `0.2.5` | [View changelog](/docs/changelog/transformers) | +| `@anvia/fastembed` | `0.2.6` | [View changelog](/docs/changelog/fastembed) | +| `@anvia/transformers` | `0.2.6` | [View changelog](/docs/changelog/transformers) | ## Vector Stores | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/chroma` | `0.2.5` | [View changelog](/docs/changelog/chroma) | -| `@anvia/milvus` | `0.3.0` | [View changelog](/docs/changelog/milvus) | -| `@anvia/pgvector` | `0.2.5` | [View changelog](/docs/changelog/pgvector) | -| `@anvia/pinecone` | `0.3.0` | [View changelog](/docs/changelog/pinecone) | -| `@anvia/qdrant` | `0.2.5` | [View changelog](/docs/changelog/qdrant) | +| `@anvia/chroma` | `0.2.6` | [View changelog](/docs/changelog/chroma) | +| `@anvia/milvus` | `0.3.1` | [View changelog](/docs/changelog/milvus) | +| `@anvia/pgvector` | `0.2.6` | [View changelog](/docs/changelog/pgvector) | +| `@anvia/pinecone` | `0.3.1` | [View changelog](/docs/changelog/pinecone) | +| `@anvia/qdrant` | `0.2.6` | [View changelog](/docs/changelog/qdrant) | ## Logger @@ -51,15 +51,15 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/langfuse` | `0.2.3` | [View changelog](/docs/changelog/langfuse) | -| `@anvia/otel` | `0.2.5` | [View changelog](/docs/changelog/otel) | +| `@anvia/langfuse` | `0.2.4` | [View changelog](/docs/changelog/langfuse) | +| `@anvia/otel` | `0.2.6` | [View changelog](/docs/changelog/otel) | ## Tools | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/sandbox` | `0.3.0` | [View changelog](/docs/changelog/sandbox) | -| `@anvia/studio` | `0.5.12` | [View changelog](/docs/changelog/studio) | +| `@anvia/sandbox` | `0.3.1` | [View changelog](/docs/changelog/sandbox) | +| `@anvia/studio` | `0.5.13` | [View changelog](/docs/changelog/studio) | ## React diff --git a/apps/docs/content/docs/changelog/langfuse.mdx b/apps/docs/content/docs/changelog/langfuse.mdx index 529a968b..269ba99e 100644 --- a/apps/docs/content/docs/changelog/langfuse.mdx +++ b/apps/docs/content/docs/changelog/langfuse.mdx @@ -9,6 +9,12 @@ Langfuse tracing adapter for Anvia. Source: [`packages/observability-langfuse/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability-langfuse/CHANGELOG.md) +## 0.2.4 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.3 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/milvus.mdx b/apps/docs/content/docs/changelog/milvus.mdx index 54b301ec..a04158a7 100644 --- a/apps/docs/content/docs/changelog/milvus.mdx +++ b/apps/docs/content/docs/changelog/milvus.mdx @@ -9,6 +9,12 @@ Milvus vector store adapter for Anvia. Source: [`packages/vector-milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-milvus/CHANGELOG.md) +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/apps/docs/content/docs/changelog/mistral.mdx b/apps/docs/content/docs/changelog/mistral.mdx index 4862032a..00a5b8f2 100644 --- a/apps/docs/content/docs/changelog/mistral.mdx +++ b/apps/docs/content/docs/changelog/mistral.mdx @@ -9,6 +9,12 @@ Mistral provider adapter for Anvia. Source: [`packages/provider-mistral/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-mistral/CHANGELOG.md) +## 0.2.7 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.6 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/openai.mdx b/apps/docs/content/docs/changelog/openai.mdx index 4c1324c5..50ba6c32 100644 --- a/apps/docs/content/docs/changelog/openai.mdx +++ b/apps/docs/content/docs/changelog/openai.mdx @@ -9,6 +9,12 @@ OpenAI provider adapter for Anvia. Source: [`packages/provider-openai/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/provider-openai/CHANGELOG.md) +## 0.3.8 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.7 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/otel.mdx b/apps/docs/content/docs/changelog/otel.mdx index d7be97cb..f2fac077 100644 --- a/apps/docs/content/docs/changelog/otel.mdx +++ b/apps/docs/content/docs/changelog/otel.mdx @@ -9,6 +9,12 @@ OpenTelemetry tracing adapter for Anvia. Source: [`packages/observability-otel/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability-otel/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pgvector.mdx b/apps/docs/content/docs/changelog/pgvector.mdx index 119b1f23..cc48df98 100644 --- a/apps/docs/content/docs/changelog/pgvector.mdx +++ b/apps/docs/content/docs/changelog/pgvector.mdx @@ -9,6 +9,12 @@ Postgres pgvector store adapter for Anvia. Source: [`packages/vector-pgvector/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-pgvector/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pinecone.mdx b/apps/docs/content/docs/changelog/pinecone.mdx index a2792ae3..e126c3c8 100644 --- a/apps/docs/content/docs/changelog/pinecone.mdx +++ b/apps/docs/content/docs/changelog/pinecone.mdx @@ -9,6 +9,12 @@ Pinecone vector store adapter for Anvia. Source: [`packages/vector-pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-pinecone/CHANGELOG.md) +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/apps/docs/content/docs/changelog/qdrant.mdx b/apps/docs/content/docs/changelog/qdrant.mdx index 402b2cbe..067b3767 100644 --- a/apps/docs/content/docs/changelog/qdrant.mdx +++ b/apps/docs/content/docs/changelog/qdrant.mdx @@ -9,6 +9,12 @@ Qdrant vector store adapter for Anvia. Source: [`packages/vector-qdrant/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-qdrant/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/sandbox.mdx b/apps/docs/content/docs/changelog/sandbox.mdx index ca629d84..3b86fd26 100644 --- a/apps/docs/content/docs/changelog/sandbox.mdx +++ b/apps/docs/content/docs/changelog/sandbox.mdx @@ -9,6 +9,12 @@ Sandboxed workspace tools for Anvia agents. Source: [`packages/tool-sandbox/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tool-sandbox/CHANGELOG.md) +## 0.3.1 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.3.0 ### Minor Changes diff --git a/apps/docs/content/docs/changelog/studio.mdx b/apps/docs/content/docs/changelog/studio.mdx index 7d43f520..aee6e67d 100644 --- a/apps/docs/content/docs/changelog/studio.mdx +++ b/apps/docs/content/docs/changelog/studio.mdx @@ -9,6 +9,12 @@ Studio UI and HTTP runtime for Anvia agents. Source: [`packages/tool-studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tool-studio/CHANGELOG.md) +## 0.5.13 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.5.12 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/transformers.mdx b/apps/docs/content/docs/changelog/transformers.mdx index c0407d10..6f0cbf3c 100644 --- a/apps/docs/content/docs/changelog/transformers.mdx +++ b/apps/docs/content/docs/changelog/transformers.mdx @@ -9,6 +9,12 @@ Transformers.js embedding model adapter for Anvia. Source: [`packages/embedding-transformers/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embedding-transformers/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- 3572881: Flatten package folders to the top-level `packages/*` workspace layout. This only updates repository layout metadata and does not change package behavior. + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/guides/cookbook.mdx b/apps/docs/content/docs/guides/cookbook.mdx index 5bb82a5e..551fedd7 100644 --- a/apps/docs/content/docs/guides/cookbook.mdx +++ b/apps/docs/content/docs/guides/cookbook.mdx @@ -13,13 +13,13 @@ Each level introduces one layer at a time: | Basics | Text calls, chat history, static context, streaming, and `ReadableStream` output | | Tools | Tool calls, streamed tool events, hooks, concurrency, conditional tools, application state, guarded tools, dynamic tool selection, and Docker sandbox tools | | Structured output | Extraction, output schemas, context, retries, and extraction with history | -| Providers and multimodal | Provider adapters, model capabilities, model listing, reasoning streams, attachments, image generation, audio generation, and transcription | +| Providers and multimodal | Provider adapters, model capabilities, model listing, reasoning streams, attachments, image generation, audio generation, transcription, and Anthropic | | Pipelines | Step transforms, composition, named parallel branches, batching, agents, extraction, and richer workflows | -| Retrieval | Embeddings, vector search, metadata filters, RAG context, document loaders, vector stores, and embedding provider variants | +| Retrieval | Embeddings, vector search, metadata filters, RAG context, document loaders, vector stores, Milvus, Pinecone, and embedding provider variants | | Multi-agent | Basic agent-tools, pipeline-backed parallel specialists, streaming agent-tools, and event stores | | Evals | Deterministic metrics, semantic similarity, custom metrics, agent eval targets, and LLM judge/score | | Studio | Single-agent, multi-agent, and subagent runners, tool approvals, questions, and Knowledge inspection | -| Integrations | MCP tools, local skills, Langfuse tracing, and Langfuse eval reporting | +| Integrations | MCP tools, local skills, Langfuse tracing, logging, and Langfuse eval reporting | ## 1. Install Dependencies @@ -35,8 +35,13 @@ Create a local `.env` file for examples that call provider APIs: OPENAI_API_KEY=... OPENAI_BASEURL=... ANTHROPIC_API_KEY=... +ANTHROPIC_BASEURL=... GEMINI_API_KEY=... MISTRAL_API_KEY=... +DATABASE_URL=... +PINECONE_API_KEY=... +PINECONE_INDEX_NAME=... +PINECONE_NAMESPACE=... ``` Anvia clients still receive credentials explicitly in code. The cookbook uses `dotenv` only as a local configuration source. @@ -78,9 +83,9 @@ pnpm cookbook:integrations:04 Legacy names such as `cookbook:basic:05`, `cookbook:intermediate:14`, `cookbook:pipeline:04`, `cookbook:rag:05`, and `cookbook:multimodal:03` remain available as aliases. -## 4. Use Chroma Examples +## 4. Use Vector Store Examples -Start ChromaDB before running the Chroma-backed RAG examples: +Start the local vector databases before running Chroma, Qdrant, pgvector, or Milvus examples: ```sh docker compose -f examples/cookbook/compose.cookbook.yml up -d @@ -88,8 +93,11 @@ pnpm cookbook:retrieval:05 pnpm cookbook:retrieval:06 pnpm cookbook:retrieval:07 pnpm cookbook:retrieval:08 +pnpm cookbook:retrieval:12 ``` +`retrieval:13` uses Pinecone. Create a 384-dimension Pinecone index first, then set `PINECONE_API_KEY`, `PINECONE_INDEX_NAME`, and optionally `PINECONE_NAMESPACE`. + Use the in-memory and Transformers examples when you do not need a separate vector database. ## 5. Map Examples to Guides @@ -100,15 +108,17 @@ Use the in-memory and Transformers examples when you do not need a separate vect | Return structured data | `structured-output:01`, `structured-output:02` | [Structured Output](/docs/guides/structured-output/schemas) | | Inspect model capabilities | `providers:03` | [Provider Clients and Models](/docs/guides/sdk-fundamentals/clients-and-models) | | List provider models | `providers:10` | [Model Listing](/docs/reference/core/model-listing) | +| Use Anthropic | `providers:11` | [Anthropic Provider](/docs/reference/providers/anthropic) | | Stream agent events | `tools:02` | [Streaming Events](/docs/guides/streaming/streaming-events) | | Stream over HTTP transports | `basics:07` | [Client Transports](/docs/guides/streaming/client-transports) | | Render reasoning summaries | `providers:04` | [Streaming Events](/docs/guides/streaming/streaming-events) | | Select dynamic tools | `tools:09` | [Tool Sets](/docs/guides/tools/tool-sets) | | Run code in a Docker sandbox | `tools:11` | [Sandbox](/docs/guides/sandbox) | | Add approval behavior | `tools:08`, `studio:03` | [Human in the Loop](/docs/guides/human-in-the-loop) | -| Add retrieval | `retrieval:01` through `retrieval:06` | [Add Retrieval](/docs/guides/learning-paths/add-retrieval) | +| Add retrieval | `retrieval:01` through `retrieval:13` | [Add Retrieval](/docs/guides/learning-paths/add-retrieval) | | Run evals | `evals:01` through `evals:05`, `integrations:04` | [Evals](/docs/guides/testing/evals) | | Generate or transcribe media | `providers:07` through `providers:09` | [Image Generation](/docs/reference/core/image-generation) | +| Add logging | `integrations:06`, `integrations:07` | [Logging](/docs/guides/observability/logging) | | Inspect locally in Studio | `studio:01`, `studio:05`, `studio:06` | [Run Studio](/docs/studio/run-studio) | Before changing public APIs, add or update a cookbook example so behavior is easy to verify from the command line. diff --git a/examples/cookbook/04_providers_and_multimodal/11-anthropic-text-call.ts b/examples/cookbook/04_providers_and_multimodal/11-anthropic-text-call.ts new file mode 100644 index 00000000..89cf3f9c --- /dev/null +++ b/examples/cookbook/04_providers_and_multimodal/11-anthropic-text-call.ts @@ -0,0 +1,19 @@ +import { AnthropicClient } from "@anvia/anthropic"; +import { AgentBuilder } from "@anvia/core/agent"; + +const client = new AnthropicClient({ + apiKey: process.env.ANTHROPIC_API_KEY, + baseUrl: process.env.ANTHROPIC_BASEURL, +}); + +const agentModel = client.completionModel( + process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514", +); + +const agent = new AgentBuilder("anthropic-agent", agentModel) + .instructions("You are a concise assistant. Answer in two sentences or less.") + .build(); + +const response = await agent.prompt("Explain what a provider adapter does.").send(); + +console.log(response.output); diff --git a/examples/cookbook/06_retrieval/12-milvus-vector-store.ts b/examples/cookbook/06_retrieval/12-milvus-vector-store.ts new file mode 100644 index 00000000..52ccdaa8 --- /dev/null +++ b/examples/cookbook/06_retrieval/12-milvus-vector-store.ts @@ -0,0 +1,41 @@ +import { embedDocuments } from "@anvia/core/embeddings"; +import { MilvusVectorStore } from "@anvia/milvus"; +import { createTransformersEmbeddingModel } from "@anvia/transformers"; + +type MarketNote = { + id: string; + text: string; + sector: string; +}; + +const embeddingModel = await createTransformersEmbeddingModel(); +const notes: MarketNote[] = [ + { + id: "cloud", + text: "Cloud infrastructure demand remained resilient into quarter end.", + sector: "technology", + }, + { + id: "rates", + text: "Rate-sensitive sectors traded lower after yields moved higher.", + sector: "macro", + }, +]; + +const embedded = await embedDocuments(embeddingModel, notes, { + id: (note) => note.id, + content: (note) => `${note.sector}: ${note.text}`, +}); + +const store = await MilvusVectorStore.connect({ + collectionName: "anvia_market_notes", + vectorSize: 384, +}); +await store.upsertDocuments(embedded); + +const results = await store.index(embeddingModel).search({ + query: "technology demand", + topK: 2, +}); + +console.log(results); diff --git a/examples/cookbook/06_retrieval/13-pinecone-vector-store.ts b/examples/cookbook/06_retrieval/13-pinecone-vector-store.ts new file mode 100644 index 00000000..f3522101 --- /dev/null +++ b/examples/cookbook/06_retrieval/13-pinecone-vector-store.ts @@ -0,0 +1,55 @@ +import { embedDocuments } from "@anvia/core/embeddings"; +import { vectorFilter } from "@anvia/core/vector-store"; +import { PineconeVectorStore } from "@anvia/pinecone"; +import { createTransformersEmbeddingModel } from "@anvia/transformers"; + +type MarketNote = { + id: string; + text: string; + sector: string; +}; + +requireEnv("PINECONE_API_KEY"); + +const embeddingModel = await createTransformersEmbeddingModel(); +const notes: MarketNote[] = [ + { + id: "cloud", + text: "Cloud infrastructure demand remained resilient into quarter end.", + sector: "technology", + }, + { + id: "rates", + text: "Rate-sensitive sectors traded lower after yields moved higher.", + sector: "macro", + }, +]; + +const embedded = await embedDocuments(embeddingModel, notes, { + id: (note) => note.id, + content: (note) => note.text, + metadata: (note) => ({ sector: note.sector }), +}); + +const store = await PineconeVectorStore.connect({ + indexName: requireEnv("PINECONE_INDEX_NAME"), + namespace: process.env.PINECONE_NAMESPACE ?? "anvia-cookbook", + createIfMissing: false, +}); +await store.upsertDocuments(embedded); + +const results = await store.index(embeddingModel).search({ + query: "technology demand", + topK: 2, + filter: vectorFilter.eq("sector", "technology"), +}); + +console.log(results); + +function requireEnv(name: string): string { + const value = process.env[name]; + if (value === undefined || value.length === 0) { + throw new Error(`Set ${name} before running this cookbook example.`); + } + return value; +} diff --git a/examples/cookbook/10_integrations/07-console-logging.ts b/examples/cookbook/10_integrations/07-console-logging.ts new file mode 100644 index 00000000..a17394b0 --- /dev/null +++ b/examples/cookbook/10_integrations/07-console-logging.ts @@ -0,0 +1,62 @@ +import { AgentBuilder } from "@anvia/core/agent"; +import { createTool } from "@anvia/core/tool"; +import { createConsoleLogger, createLoggerObserver } from "@anvia/logger"; +import { OpenAIClient } from "@anvia/openai"; +import { z } from "zod"; + +const logger = createConsoleLogger({ + name: "anvia-cookbook", + level: process.env.LOG_LEVEL === "debug" ? "debug" : "info", + bindings: { example: "integrations:07" }, +}); + +logger.child({ component: "setup" }).info("console logger configured"); + +const client = new OpenAIClient({ + baseUrl: process.env.OPENAI_BASEURL, + apiKey: process.env.OPENAI_API_KEY, +}); + +const lookupTicket = createTool({ + name: "lookup_ticket", + description: "Read a support ticket from local application state.", + input: z.object({ + id: z.string().describe("The ticket id to read."), + }), + output: z.object({ + id: z.string(), + title: z.string(), + status: z.enum(["open", "pending", "closed"]), + summary: z.string(), + }), + execute: ({ id }) => ({ + id, + title: "Search indexing delay after content publish", + status: "pending" as const, + summary: "New articles are visible in the CMS but missing from the customer search index.", + }), +}); + +const agent = new AgentBuilder("support-console-logger-demo", client.completionModel("gpt-5.5")) + .instructions("Use tools when useful. Answer with a short engineering-focused summary.") + .observe( + createLoggerObserver(logger, { + includeToolResult: true, + }), + ) + .tool(lookupTicket) + .defaultMaxTurns(2) + .build(); + +const response = await agent + .prompt("Summarize ticket TICKET-2002 for the search team.") + .withTrace({ + name: "support-ticket-console-logging", + userId: "cookbook-user", + sessionId: "cookbook-session", + metadata: { ticketId: "TICKET-2002", example: "integrations:07" }, + tags: ["cookbook", "logging"], + }) + .send(); + +console.log(response.output); diff --git a/examples/cookbook/README.md b/examples/cookbook/README.md index 14f78783..b133e104 100644 --- a/examples/cookbook/README.md +++ b/examples/cookbook/README.md @@ -38,19 +38,24 @@ Create a repository-root `.env` for examples that call provider APIs: ```sh OPENAI_API_KEY=... OPENAI_BASEURL=... +ANTHROPIC_API_KEY=... +ANTHROPIC_BASEURL=... GEMINI_API_KEY=... MISTRAL_API_KEY=... LANGFUSE_PUBLIC_KEY=... LANGFUSE_SECRET_KEY=... LANGFUSE_BASE_URL=... DATABASE_URL=... +PINECONE_API_KEY=... +PINECONE_INDEX_NAME=... +PINECONE_NAMESPACE=... ``` Not every example needs every variable. Pure pipeline, dynamic tool, and core eval examples run without provider credentials. ## External Services and Side Effects -- Chroma, Qdrant, and pgvector examples use `compose.cookbook.yml` from the cookbook directory: +- Chroma, Qdrant, pgvector, and Milvus examples use `compose.cookbook.yml` from the cookbook directory: ```sh docker compose -f examples/cookbook/compose.cookbook.yml up -d @@ -58,11 +63,13 @@ Not every example needs every variable. Pure pipeline, dynamic tool, and core ev pnpm cookbook:retrieval:06 pnpm cookbook:retrieval:07 pnpm cookbook:retrieval:08 + pnpm cookbook:retrieval:12 ``` - `retrieval:08` uses the compose pgvector connection on host port `5439` by default. Set `DATABASE_URL` to point it at another Postgres database. +- `retrieval:13` uses a hosted Pinecone index. Create a 384-dimension index first, then set `PINECONE_API_KEY`, `PINECONE_INDEX_NAME`, and optionally `PINECONE_NAMESPACE`. - Langfuse examples need Langfuse credentials and live in `10_integrations`. -- `integrations:06` logs agent lifecycle events with `@anvia/logger`. +- `integrations:06` logs agent lifecycle events with Pino through `@anvia/logger`; `integrations:07` shows the built-in console logger. - Studio examples start a local HTTP server and keep Studio state in memory by default. `studio:10` shows explicit SQLite store wiring for sessions, traces, pipeline logs, and pipeline run history. - Tool history and loader examples write sample files under `.memory`. - Image and audio generation examples write generated media files in the current working directory. diff --git a/examples/cookbook/compose.cookbook.yml b/examples/cookbook/compose.cookbook.yml index 68ddf79b..616cc4a4 100644 --- a/examples/cookbook/compose.cookbook.yml +++ b/examples/cookbook/compose.cookbook.yml @@ -55,7 +55,34 @@ services: timeout: 3s retries: 20 + milvus: + image: milvusdb/milvus:v2.5.17 + command: ["milvus", "run", "standalone"] + security_opt: + - seccomp:unconfined + environment: + ETCD_USE_EMBED: "true" + ETCD_DATA_DIR: /var/lib/milvus/etcd + COMMON_STORAGETYPE: local + ports: + - "19530:19530" + - "9091:9091" + volumes: + - milvus-data:/var/lib/milvus + healthcheck: + test: + [ + "CMD", + "bash", + "-lc", + "curl -sf http://127.0.0.1:9091/healthz", + ] + interval: 5s + timeout: 3s + retries: 30 + volumes: chroma-data: qdrant-data: pgvector-data: + milvus-data: diff --git a/examples/cookbook/package.json b/examples/cookbook/package.json index b1e2323f..d2402ba0 100644 --- a/examples/cookbook/package.json +++ b/examples/cookbook/package.json @@ -40,6 +40,7 @@ "providers:08": "tsx -r dotenv/config 04_providers_and_multimodal/08-openai-audio-and-transcription.ts dotenv_config_path=../../.env", "providers:09": "tsx -r dotenv/config 04_providers_and_multimodal/09-gemini-image-and-transcription.ts dotenv_config_path=../../.env", "providers:10": "tsx -r dotenv/config 04_providers_and_multimodal/10-list-models.ts dotenv_config_path=../../.env", + "providers:11": "tsx -r dotenv/config 04_providers_and_multimodal/11-anthropic-text-call.ts dotenv_config_path=../../.env", "providers-and-multimodal": "tsx -r dotenv/config 04_providers_and_multimodal/01-gemini-text-call.ts dotenv_config_path=../../.env", "pipelines": "tsx -r dotenv/config 05_pipelines/01-step-transform.ts dotenv_config_path=../../.env", "pipelines:01": "tsx -r dotenv/config 05_pipelines/01-step-transform.ts dotenv_config_path=../../.env", @@ -63,6 +64,8 @@ "retrieval:09": "tsx -r dotenv/config 06_retrieval/09-local-minilm-rag.ts dotenv_config_path=../../.env", "retrieval:10": "tsx -r dotenv/config 06_retrieval/10-fastembed-rag.ts dotenv_config_path=../../.env", "retrieval:11": "tsx -r dotenv/config 06_retrieval/11-mistral-embeddings-rag.ts dotenv_config_path=../../.env", + "retrieval:12": "tsx -r dotenv/config 06_retrieval/12-milvus-vector-store.ts dotenv_config_path=../../.env", + "retrieval:13": "tsx -r dotenv/config 06_retrieval/13-pinecone-vector-store.ts dotenv_config_path=../../.env", "multi-agent": "tsx -r dotenv/config 07_multi_agent/01-agent-as-tool.ts dotenv_config_path=../../.env", "multi-agent:01": "tsx -r dotenv/config 07_multi_agent/01-agent-as-tool.ts dotenv_config_path=../../.env", "multi-agent:02": "tsx -r dotenv/config 07_multi_agent/02-parallel-specialists.ts dotenv_config_path=../../.env", @@ -94,6 +97,7 @@ "integrations:04": "tsx -r dotenv/config 10_integrations/04-langfuse-eval-reporting.ts dotenv_config_path=../../.env", "integrations:05": "tsx -r dotenv/config 10_integrations/05-otel-tracing.ts dotenv_config_path=../../.env", "integrations:06": "tsx -r dotenv/config 10_integrations/06-agent-logging.ts dotenv_config_path=../../.env", + "integrations:07": "tsx -r dotenv/config 10_integrations/07-console-logging.ts dotenv_config_path=../../.env", "basic": "tsx -r dotenv/config 01_basics/01-text-call.ts dotenv_config_path=../../.env", "basic:01": "tsx -r dotenv/config 01_basics/01-text-call.ts dotenv_config_path=../../.env", "basic:02": "tsx -r dotenv/config 01_basics/02-chat-history.ts dotenv_config_path=../../.env", @@ -151,16 +155,19 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@anvia/anthropic": "workspace:*", "@anvia/chroma": "workspace:*", "@anvia/core": "workspace:*", "@anvia/fastembed": "workspace:*", "@anvia/gemini": "workspace:*", "@anvia/langfuse": "workspace:*", "@anvia/logger": "workspace:*", + "@anvia/milvus": "workspace:*", "@anvia/mistral": "workspace:*", "@anvia/otel": "workspace:*", "@anvia/openai": "workspace:*", "@anvia/pgvector": "workspace:*", + "@anvia/pinecone": "workspace:*", "@anvia/qdrant": "workspace:*", "@anvia/react": "workspace:*", "@anvia/sandbox": "workspace:*", diff --git a/examples/cookbook/tsconfig.json b/examples/cookbook/tsconfig.json index 05cebe60..9fbc586f 100644 --- a/examples/cookbook/tsconfig.json +++ b/examples/cookbook/tsconfig.json @@ -7,6 +7,7 @@ "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "paths": { + "@anvia/anthropic": ["../../packages/provider-anthropic/src/index.ts"], "@anvia/chroma": ["../../packages/vector-chroma/src/index.ts"], "@anvia/core": ["../../packages/core/src/index.ts"], "@anvia/core/internal/agent": ["../../packages/core/src/internal/agent.ts"], @@ -15,10 +16,12 @@ "@anvia/gemini": ["../../packages/provider-gemini/src/index.ts"], "@anvia/langfuse": ["../../packages/observability-langfuse/src/index.ts"], "@anvia/logger": ["../../packages/logger/src/index.ts"], + "@anvia/milvus": ["../../packages/vector-milvus/src/index.ts"], "@anvia/mistral": ["../../packages/provider-mistral/src/index.ts"], "@anvia/openai": ["../../packages/provider-openai/src/index.ts"], "@anvia/otel": ["../../packages/observability-otel/src/index.ts"], "@anvia/pgvector": ["../../packages/vector-pgvector/src/index.ts"], + "@anvia/pinecone": ["../../packages/vector-pinecone/src/index.ts"], "@anvia/qdrant": ["../../packages/vector-qdrant/src/index.ts"], "@anvia/react": ["../../packages/react/src/index.ts"], "@anvia/server": ["../../packages/server/src/index.ts"], diff --git a/package.json b/package.json index 367cea0a..0753cd2a 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "cookbook:providers:08": "pnpm --filter cookbook providers:08", "cookbook:providers:09": "pnpm --filter cookbook providers:09", "cookbook:providers:10": "pnpm --filter cookbook providers:10", + "cookbook:providers:11": "pnpm --filter cookbook providers:11", "cookbook:providers-and-multimodal": "pnpm --filter cookbook providers-and-multimodal", "cookbook:pipelines": "pnpm --filter cookbook pipelines", "cookbook:pipelines:01": "pnpm --filter cookbook pipelines:01", @@ -69,6 +70,8 @@ "cookbook:retrieval:09": "pnpm --filter cookbook retrieval:09", "cookbook:retrieval:10": "pnpm --filter cookbook retrieval:10", "cookbook:retrieval:11": "pnpm --filter cookbook retrieval:11", + "cookbook:retrieval:12": "pnpm --filter cookbook retrieval:12", + "cookbook:retrieval:13": "pnpm --filter cookbook retrieval:13", "cookbook:multi-agent": "pnpm --filter cookbook multi-agent", "cookbook:multi-agent:01": "pnpm --filter cookbook multi-agent:01", "cookbook:multi-agent:02": "pnpm --filter cookbook multi-agent:02", @@ -100,6 +103,7 @@ "cookbook:integrations:04": "pnpm --filter cookbook integrations:04", "cookbook:integrations:05": "pnpm --filter cookbook integrations:05", "cookbook:integrations:06": "pnpm --filter cookbook integrations:06", + "cookbook:integrations:07": "pnpm --filter cookbook integrations:07", "cookbook:basic": "pnpm --filter cookbook basic", "cookbook:basic:01": "pnpm --filter cookbook basic:01", "cookbook:basic:02": "pnpm --filter cookbook basic:02", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55451267..25a0aee6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,6 +154,9 @@ importers: examples/cookbook: dependencies: + '@anvia/anthropic': + specifier: workspace:* + version: link:../../packages/provider-anthropic '@anvia/chroma': specifier: workspace:* version: link:../../packages/vector-chroma @@ -172,6 +175,9 @@ importers: '@anvia/logger': specifier: workspace:* version: link:../../packages/logger + '@anvia/milvus': + specifier: workspace:* + version: link:../../packages/vector-milvus '@anvia/mistral': specifier: workspace:* version: link:../../packages/provider-mistral @@ -184,6 +190,9 @@ importers: '@anvia/pgvector': specifier: workspace:* version: link:../../packages/vector-pgvector + '@anvia/pinecone': + specifier: workspace:* + version: link:../../packages/vector-pinecone '@anvia/qdrant': specifier: workspace:* version: link:../../packages/vector-qdrant From 4806f3e23d1c6beb2e975343defd240be502674a Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:55:27 +0700 Subject: [PATCH 09/16] Add prompt request runtime steering (#110) --- .changeset/gentle-prompt-steering.md | 5 + packages/core/src/agent/request.ts | 68 ++++++++++ packages/core/test/prompt-request.test.ts | 63 ++++++++++ packages/core/test/streaming.test.ts | 146 +++++++++++++++++++++- 4 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 .changeset/gentle-prompt-steering.md diff --git a/.changeset/gentle-prompt-steering.md b/.changeset/gentle-prompt-steering.md new file mode 100644 index 00000000..3d0b3469 --- /dev/null +++ b/.changeset/gentle-prompt-steering.md @@ -0,0 +1,5 @@ +--- +"@anvia/core": patch +--- + +Add `PromptRequest.steer()` for enqueueing user messages at safe model-turn boundaries during active prompt runs. diff --git a/packages/core/src/agent/request.ts b/packages/core/src/agent/request.ts index 56d39313..26159ddf 100644 --- a/packages/core/src/agent/request.ts +++ b/packages/core/src/agent/request.ts @@ -115,6 +115,8 @@ export class PromptRequest { private concurrency = 1; private traceOptions: AgentTraceOptions | undefined; private requestMiddlewares: AgentMiddleware[] = []; + private readonly steeringMessages: MessageType[] = []; + private runState: "idle" | "running" | "completed" | "errored" | "cancelled" = "idle"; private readonly memoryRecorder: PromptRequestMemory; private constructor( @@ -189,7 +191,17 @@ export class PromptRequest { return this; } + steer(input: string | MessageType | MessageType[]): boolean { + if (this.isTerminal()) { + return false; + } + + this.steeringMessages.push(...normalizeSteeringInput(input)); + return true; + } + async send(): Promise { + this.startRun(); const runId = globalThis.crypto.randomUUID(); const newMessages: MessageType[] = [this.promptMessage]; this.chatHistory = await this.memoryRecorder.prepareRun(runId, newMessages); @@ -254,6 +266,13 @@ export class PromptRequest { (item): item is ToolCall => item.type === "tool_call", ); if (toolCalls.length === 0) { + if ( + await this.drainSteeringMessages(runId, currentTurns, newMessages, pendingTurnMessages) + ) { + await this.memoryRecorder.commitCompletedTurn(runId, currentTurns, pendingTurnMessages); + continue; + } + await this.memoryRecorder.commitCompletedRun( runId, currentTurns, @@ -266,6 +285,7 @@ export class PromptRequest { messages: [...newMessages], trace: runObservers.trace, }; + this.runState = "completed"; await this.runRunEndHook(result, newMessages); await runObservers.end(result); return result; @@ -290,12 +310,14 @@ export class PromptRequest { [toolMessage], pendingTurnMessages, ); + await this.drainSteeringMessages(runId, currentTurns, newMessages, pendingTurnMessages); await this.memoryRecorder.commitCompletedTurn(runId, currentTurns, pendingTurnMessages); } throw new MaxTurnsError(this.maxTurnCount, [...this.chatHistory, ...newMessages], lastPrompt); } catch (error) { const finalError = await this.runRunErrorHook(error, usage, newMessages); + this.runState = finalError instanceof PromptCancelledError ? "cancelled" : "errored"; await runObservers.error({ error: finalError, usage, messages: [...newMessages] }); await this.memoryRecorder.recordError(runId, finalError, newMessages); throw finalError; @@ -307,6 +329,7 @@ export class PromptRequest { throw new Error("This completion model does not support streaming"); } + this.startRun(); const runId = globalThis.crypto.randomUUID(); const newMessages: MessageType[] = [this.promptMessage]; this.chatHistory = await this.memoryRecorder.prepareRun(runId, newMessages); @@ -419,6 +442,13 @@ export class PromptRequest { yield await emit({ type: "turn_end", turn: currentTurns, response }); if (toolCalls.length === 0) { + if ( + await this.drainSteeringMessages(runId, currentTurns, newMessages, pendingTurnMessages) + ) { + await this.memoryRecorder.commitCompletedTurn(runId, currentTurns, pendingTurnMessages); + continue; + } + const output = textFromAssistantContent(response.choice); await this.memoryRecorder.commitCompletedRun( runId, @@ -434,6 +464,7 @@ export class PromptRequest { messages: [...newMessages], trace: runObservers.trace, }); + this.runState = "completed"; await this.runRunEndHook({ output, usage, messages: [...newMessages] }, newMessages); await runObservers.end({ output, usage, messages: [...newMessages] }); return; @@ -471,12 +502,14 @@ export class PromptRequest { [toolMessage], pendingTurnMessages, ); + await this.drainSteeringMessages(runId, currentTurns, newMessages, pendingTurnMessages); await this.memoryRecorder.commitCompletedTurn(runId, currentTurns, pendingTurnMessages); } throw new MaxTurnsError(this.maxTurnCount, [...this.chatHistory, ...newMessages], lastPrompt); } catch (error) { const finalError = await this.runRunErrorHook(error, usage, newMessages); + this.runState = finalError instanceof PromptCancelledError ? "cancelled" : "errored"; await runObservers.error({ error: finalError, usage, messages: [...newMessages] }); await this.memoryRecorder.recordError(runId, finalError, newMessages); yield await emit({ type: "error", error: finalError }); @@ -758,6 +791,34 @@ export class PromptRequest { return [...this.agent.middlewares, ...this.requestMiddlewares]; } + private async drainSteeringMessages( + runId: string, + turn: number, + newMessages: MessageType[], + pendingTurnMessages: MessageType[], + ): Promise { + const messages = this.steeringMessages.splice(0); + if (messages.length === 0) { + return false; + } + + newMessages.push(...messages); + await this.memoryRecorder.commitMessages(runId, turn, messages, pendingTurnMessages); + return true; + } + + private startRun(): void { + if (!this.isTerminal()) { + this.runState = "running"; + } + } + + private isTerminal(): boolean { + return ( + this.runState === "completed" || this.runState === "errored" || this.runState === "cancelled" + ); + } + private cancelled(newMessages: MessageType[], reason: string): PromptCancelledError { return new PromptCancelledError([...this.chatHistory, ...newMessages], reason); } @@ -786,6 +847,13 @@ function normalizePromptInput(prompt: string | MessageType | MessageType[]): { }; } +function normalizeSteeringInput(input: string | MessageType | MessageType[]): MessageType[] { + if (typeof input === "string") { + return [Message.user(input)]; + } + return Array.isArray(input) ? [...input] : [input]; +} + function addTurn(turn: number, event: AgentDeltaEvent): AgentStreamEvent { if (event.type === "text_delta") { return { type: "text_delta", turn, delta: event.delta }; diff --git a/packages/core/test/prompt-request.test.ts b/packages/core/test/prompt-request.test.ts index ee12f235..d479de4a 100644 --- a/packages/core/test/prompt-request.test.ts +++ b/packages/core/test/prompt-request.test.ts @@ -83,6 +83,39 @@ describe("PromptRequest", () => { expect(model.requests[0]?.chatHistory[0]).toEqual(Message.user("hello")); }); + it("send consumes steering queued before no-tool finalization", async () => { + const model = new QueueModel([ + response([AssistantContent.text("first")]), + response([AssistantContent.text("second")]), + ]); + let steer: ((input: string) => boolean) | undefined; + const agent = new AgentBuilder("test-agent", model) + .hook( + createHook({ + onTurnEnd({ turn }) { + if (turn === 1) { + expect(steer?.("revise")).toBe(true); + } + }, + }), + ) + .build(); + const request = agent.prompt("hello"); + steer = request.steer.bind(request); + + const result = await request.send(); + + expect(result.output).toBe("second"); + expect(model.requests).toHaveLength(2); + expect(model.requests[1]?.chatHistory.at(-1)).toEqual(Message.user("revise")); + expect(result.messages).toEqual([ + Message.user("hello"), + Message.assistant("first"), + Message.user("revise"), + Message.assistant("second"), + ]); + }); + it("merges repeated instruction blocks", async () => { const model = new QueueModel([response([AssistantContent.text("done")])]); const agent = new AgentBuilder("test-agent", model) @@ -723,6 +756,36 @@ describe("PromptRequest", () => { expect(events).toEqual(["completion_error:No queued response", "run_error:No queued response"]); }); + it("rejects steering after final, error, and cancel", async () => { + const finalModel = new QueueModel([response([AssistantContent.text("done")])]); + const finalAgent = new AgentBuilder("test-agent", finalModel).build(); + const finalRequest = finalAgent.prompt("hello"); + + const result = await finalRequest.send(); + const finalMessages = [...result.messages]; + + expect(finalRequest.steer("late")).toBe(false); + expect(result.messages).toEqual(finalMessages); + expect(finalModel.requests).toHaveLength(1); + + const errorRequest = new AgentBuilder("test-agent", new QueueModel([])).build().prompt("hello"); + await expect(errorRequest.send()).rejects.toThrow("No queued response"); + expect(errorRequest.steer("late")).toBe(false); + + const cancelRequest = new AgentBuilder("test-agent", new QueueModel([])) + .hook( + createHook({ + onRunStart() { + return cancelPrompt("stop"); + }, + }), + ) + .build() + .prompt("hello"); + await expect(cancelRequest.send()).rejects.toBeInstanceOf(PromptCancelledError); + expect(cancelRequest.steer("late")).toBe(false); + }); + it("runs tool error hooks and keeps tool errors as model-visible results", async () => { const failingTool = createTool({ name: "fail", diff --git a/packages/core/test/streaming.test.ts b/packages/core/test/streaming.test.ts index 43ffbd32..0a27c593 100644 --- a/packages/core/test/streaming.test.ts +++ b/packages/core/test/streaming.test.ts @@ -32,7 +32,11 @@ class StreamingQueueModel implements StreamingCompletionModel { }; readonly requests: CompletionRequest[] = []; - constructor(private readonly responses: CompletionStreamEvent[][]) {} + constructor( + private readonly responses: Array< + Iterable | AsyncIterable + >, + ) {} async completion(): Promise { throw new Error("completion should not be called"); @@ -44,7 +48,9 @@ class StreamingQueueModel implements StreamingCompletionModel { if (response === undefined) { throw new Error("No queued response"); } - yield* response; + for await (const event of response) { + yield event; + } } } @@ -101,6 +107,142 @@ describe("PromptRequest streaming", () => { expect(model.requests[0]?.chatHistory[0]).toEqual(Message.user("hi")); }); + it("continues when steering arrives before a no-tool response finalizes", async () => { + const model = new StreamingQueueModel([ + [{ type: "text_delta", delta: "first" }], + [{ type: "text_delta", delta: "second" }], + ]); + const agent = new AgentBuilder("test-agent", model).build(); + const request = agent.prompt("hi"); + const iterator = request.stream()[Symbol.asyncIterator](); + + expect(await nextEvent(iterator)).toMatchObject({ + type: "turn_start", + turn: 1, + prompt: Message.user("hi"), + }); + expect(await nextEvent(iterator)).toMatchObject({ + type: "text_delta", + turn: 1, + delta: "first", + }); + expect(await nextEvent(iterator)).toMatchObject({ type: "turn_end", turn: 1 }); + + expect(request.steer("revise")).toBe(true); + + const rest = await collectIterator(iterator); + expect(rest.map((event) => event.type)).toEqual([ + "turn_start", + "text_delta", + "turn_end", + "final", + ]); + expect(rest[0]).toMatchObject({ + type: "turn_start", + turn: 2, + prompt: Message.user("revise"), + }); + expect(model.requests).toHaveLength(2); + expect(model.requests[1]?.chatHistory.at(-1)).toEqual(Message.user("revise")); + expect(rest.at(-1)).toMatchObject({ + type: "final", + output: "second", + messages: [ + Message.user("hi"), + Message.assistant("first"), + Message.user("revise"), + Message.assistant("second"), + ], + }); + }); + + it("appends steering after tool results before the next completion turn", async () => { + const toolStarted = deferred(); + const toolRelease = deferred(); + const slowAddTool = createTool({ + name: "slow_add", + description: "Add numbers slowly", + input: z.object({ + x: z.number(), + y: z.number(), + }), + output: z.number(), + async execute(args) { + toolStarted.resolve(); + await toolRelease.promise; + return args.x + args.y; + }, + }); + const toolCall = AssistantContent.toolCall("call_1", "slow_add", { x: 2, y: 5 }); + const model = new StreamingQueueModel([ + [{ type: "tool_call", toolCall }], + [{ type: "text_delta", delta: "done" }], + ]); + const agent = new AgentBuilder("test-agent", model).tool(slowAddTool).build(); + const request = agent.prompt("add"); + const eventsPromise = collect(request.stream()); + + await toolStarted.promise; + expect(request.steer("also explain")).toBe(true); + toolRelease.resolve(7); + + const events = await eventsPromise; + expect(events.at(-1)).toMatchObject({ type: "final", output: "done" }); + expect(model.requests).toHaveLength(2); + expect(model.requests[1]?.chatHistory.slice(-3)).toEqual([ + Message.assistant([toolCall]), + Message.tool([ + { + type: "tool_result", + id: "call_1", + content: [{ type: "text", text: "7" }], + }, + ]), + Message.user("also explain"), + ]); + }); + + it("consumes multiple steering calls in FIFO order", async () => { + const model = new StreamingQueueModel([ + [{ type: "text_delta", delta: "base" }], + [{ type: "text_delta", delta: "done" }], + ]); + const agent = new AgentBuilder("test-agent", model).build(); + const request = agent.prompt("start"); + const iterator = request.stream()[Symbol.asyncIterator](); + const firstSteer = Message.user("first steer"); + const secondSteer = Message.user("second steer"); + + expect(await nextEvent(iterator)).toMatchObject({ type: "turn_start", turn: 1 }); + expect(await nextEvent(iterator)).toMatchObject({ type: "text_delta", turn: 1 }); + expect(await nextEvent(iterator)).toMatchObject({ type: "turn_end", turn: 1 }); + + expect(request.steer(firstSteer)).toBe(true); + expect(request.steer([secondSteer])).toBe(true); + + const rest = await collectIterator(iterator); + expect(rest[0]).toMatchObject({ + type: "turn_start", + turn: 2, + prompt: secondSteer, + }); + expect(model.requests[1]?.chatHistory.slice(-3)).toEqual([ + Message.assistant("base"), + firstSteer, + secondSteer, + ]); + expect(rest.at(-1)).toMatchObject({ + type: "final", + messages: [ + Message.user("start"), + Message.assistant("base"), + firstSteer, + secondSteer, + Message.assistant("done"), + ], + }); + }); + it("merges usage-only final stream responses with accumulated text", async () => { const model = new StreamingQueueModel([ [ From 3a53eda94597c871c41dd1e2ba1312ba9a886019 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:59:01 +0700 Subject: [PATCH 10/16] Version Packages (#111) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/gentle-prompt-steering.md | 5 ----- packages/core/CHANGELOG.md | 6 ++++++ packages/core/package.json | 2 +- packages/embedding-fastembed/CHANGELOG.md | 7 +++++++ packages/embedding-fastembed/package.json | 2 +- packages/embedding-transformers/CHANGELOG.md | 7 +++++++ packages/embedding-transformers/package.json | 2 +- packages/logger/CHANGELOG.md | 7 +++++++ packages/logger/package.json | 2 +- packages/observability-otel/CHANGELOG.md | 7 +++++++ packages/observability-otel/package.json | 2 +- packages/provider-mistral/CHANGELOG.md | 7 +++++++ packages/provider-mistral/package.json | 2 +- packages/provider-openai/CHANGELOG.md | 7 +++++++ packages/provider-openai/package.json | 2 +- packages/tool-sandbox/CHANGELOG.md | 7 +++++++ packages/tool-sandbox/package.json | 2 +- packages/tool-studio/CHANGELOG.md | 7 +++++++ packages/tool-studio/package.json | 2 +- packages/vector-chroma/CHANGELOG.md | 7 +++++++ packages/vector-chroma/package.json | 2 +- packages/vector-milvus/CHANGELOG.md | 7 +++++++ packages/vector-milvus/package.json | 2 +- packages/vector-pgvector/CHANGELOG.md | 7 +++++++ packages/vector-pgvector/package.json | 2 +- packages/vector-pinecone/CHANGELOG.md | 7 +++++++ packages/vector-pinecone/package.json | 2 +- packages/vector-qdrant/CHANGELOG.md | 7 +++++++ packages/vector-qdrant/package.json | 2 +- 29 files changed, 111 insertions(+), 19 deletions(-) delete mode 100644 .changeset/gentle-prompt-steering.md diff --git a/.changeset/gentle-prompt-steering.md b/.changeset/gentle-prompt-steering.md deleted file mode 100644 index 3d0b3469..00000000 --- a/.changeset/gentle-prompt-steering.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@anvia/core": patch ---- - -Add `PromptRequest.steer()` for enqueueing user messages at safe model-turn boundaries during active prompt runs. diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 6534a32d..d63acbbb 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # @anvia/core +## 0.6.2 + +### Patch Changes + +- 4806f3e: Add `PromptRequest.steer()` for enqueueing user messages at safe model-turn boundaries during active prompt runs. + ## 0.6.1 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index b0925d94..b459f733 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/core", - "version": "0.6.1", + "version": "0.6.2", "description": "Core runtime primitives for context-aware Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/embedding-fastembed/CHANGELOG.md b/packages/embedding-fastembed/CHANGELOG.md index 020a832a..853f2668 100644 --- a/packages/embedding-fastembed/CHANGELOG.md +++ b/packages/embedding-fastembed/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/fastembed +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/embedding-fastembed/package.json b/packages/embedding-fastembed/package.json index 0847a1aa..a9c80c44 100644 --- a/packages/embedding-fastembed/package.json +++ b/packages/embedding-fastembed/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/fastembed", - "version": "0.2.6", + "version": "0.2.7", "description": "FastEmbed embedding model adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/embedding-transformers/CHANGELOG.md b/packages/embedding-transformers/CHANGELOG.md index 2bc992e7..b6ffd4cb 100644 --- a/packages/embedding-transformers/CHANGELOG.md +++ b/packages/embedding-transformers/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/transformers +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/embedding-transformers/package.json b/packages/embedding-transformers/package.json index bd423ade..e7209a2f 100644 --- a/packages/embedding-transformers/package.json +++ b/packages/embedding-transformers/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/transformers", - "version": "0.2.6", + "version": "0.2.7", "description": "Transformers.js embedding model adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md index 2633c538..3d18a8ec 100644 --- a/packages/logger/CHANGELOG.md +++ b/packages/logger/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/logger +## 0.3.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.3.6 ### Patch Changes diff --git a/packages/logger/package.json b/packages/logger/package.json index f2fe70de..1fa55d4a 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/logger", - "version": "0.3.6", + "version": "0.3.7", "description": "Structured logger adapters for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/observability-otel/CHANGELOG.md b/packages/observability-otel/CHANGELOG.md index bcc336bd..8343f01d 100644 --- a/packages/observability-otel/CHANGELOG.md +++ b/packages/observability-otel/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/otel +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/observability-otel/package.json b/packages/observability-otel/package.json index 314a6564..484f2f28 100644 --- a/packages/observability-otel/package.json +++ b/packages/observability-otel/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/otel", - "version": "0.2.6", + "version": "0.2.7", "description": "OpenTelemetry tracing adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-mistral/CHANGELOG.md b/packages/provider-mistral/CHANGELOG.md index 829c1249..01b80167 100644 --- a/packages/provider-mistral/CHANGELOG.md +++ b/packages/provider-mistral/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/mistral +## 0.2.8 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.7 ### Patch Changes diff --git a/packages/provider-mistral/package.json b/packages/provider-mistral/package.json index 43d43d59..7e5676d9 100644 --- a/packages/provider-mistral/package.json +++ b/packages/provider-mistral/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/mistral", - "version": "0.2.7", + "version": "0.2.8", "description": "Mistral provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/provider-openai/CHANGELOG.md b/packages/provider-openai/CHANGELOG.md index 8302d9fd..78cbb6f2 100644 --- a/packages/provider-openai/CHANGELOG.md +++ b/packages/provider-openai/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/openai +## 0.3.9 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.3.8 ### Patch Changes diff --git a/packages/provider-openai/package.json b/packages/provider-openai/package.json index 225f9aa3..8d9b5610 100644 --- a/packages/provider-openai/package.json +++ b/packages/provider-openai/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/openai", - "version": "0.3.8", + "version": "0.3.9", "description": "OpenAI provider adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/tool-sandbox/CHANGELOG.md b/packages/tool-sandbox/CHANGELOG.md index 04392902..405b384f 100644 --- a/packages/tool-sandbox/CHANGELOG.md +++ b/packages/tool-sandbox/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/sandbox +## 0.3.2 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.3.1 ### Patch Changes diff --git a/packages/tool-sandbox/package.json b/packages/tool-sandbox/package.json index 2c0c917e..0c51f4b9 100644 --- a/packages/tool-sandbox/package.json +++ b/packages/tool-sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/sandbox", - "version": "0.3.1", + "version": "0.3.2", "description": "Sandboxed workspace tools for Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/tool-studio/CHANGELOG.md b/packages/tool-studio/CHANGELOG.md index 3fa34d3c..a428b36f 100644 --- a/packages/tool-studio/CHANGELOG.md +++ b/packages/tool-studio/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/studio +## 0.5.14 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.5.13 ### Patch Changes diff --git a/packages/tool-studio/package.json b/packages/tool-studio/package.json index fd1c5256..497a9604 100644 --- a/packages/tool-studio/package.json +++ b/packages/tool-studio/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/studio", - "version": "0.5.13", + "version": "0.5.14", "description": "Studio UI and HTTP runtime for Anvia agents.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-chroma/CHANGELOG.md b/packages/vector-chroma/CHANGELOG.md index 80b37582..5342dccb 100644 --- a/packages/vector-chroma/CHANGELOG.md +++ b/packages/vector-chroma/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/chroma +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/vector-chroma/package.json b/packages/vector-chroma/package.json index 731f7f5a..972e5cb1 100644 --- a/packages/vector-chroma/package.json +++ b/packages/vector-chroma/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/chroma", - "version": "0.2.6", + "version": "0.2.7", "description": "ChromaDB vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-milvus/CHANGELOG.md b/packages/vector-milvus/CHANGELOG.md index 7103e5ad..54ed77a9 100644 --- a/packages/vector-milvus/CHANGELOG.md +++ b/packages/vector-milvus/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/milvus +## 0.3.2 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.3.1 ### Patch Changes diff --git a/packages/vector-milvus/package.json b/packages/vector-milvus/package.json index d7faa8a8..584564fc 100644 --- a/packages/vector-milvus/package.json +++ b/packages/vector-milvus/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/milvus", - "version": "0.3.1", + "version": "0.3.2", "description": "Milvus vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-pgvector/CHANGELOG.md b/packages/vector-pgvector/CHANGELOG.md index 4e1c1321..da39f4bc 100644 --- a/packages/vector-pgvector/CHANGELOG.md +++ b/packages/vector-pgvector/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/pgvector +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/vector-pgvector/package.json b/packages/vector-pgvector/package.json index d1c41f93..1e61668a 100644 --- a/packages/vector-pgvector/package.json +++ b/packages/vector-pgvector/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/pgvector", - "version": "0.2.6", + "version": "0.2.7", "description": "Postgres pgvector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-pinecone/CHANGELOG.md b/packages/vector-pinecone/CHANGELOG.md index 5151eb98..d900e83b 100644 --- a/packages/vector-pinecone/CHANGELOG.md +++ b/packages/vector-pinecone/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/pinecone +## 0.3.2 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.3.1 ### Patch Changes diff --git a/packages/vector-pinecone/package.json b/packages/vector-pinecone/package.json index ff74c284..d89ff1b3 100644 --- a/packages/vector-pinecone/package.json +++ b/packages/vector-pinecone/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/pinecone", - "version": "0.3.1", + "version": "0.3.2", "description": "Pinecone vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", diff --git a/packages/vector-qdrant/CHANGELOG.md b/packages/vector-qdrant/CHANGELOG.md index cac29570..177e2093 100644 --- a/packages/vector-qdrant/CHANGELOG.md +++ b/packages/vector-qdrant/CHANGELOG.md @@ -1,5 +1,12 @@ # @anvia/qdrant +## 0.2.7 + +### Patch Changes + +- Updated dependencies [4806f3e] + - @anvia/core@0.6.2 + ## 0.2.6 ### Patch Changes diff --git a/packages/vector-qdrant/package.json b/packages/vector-qdrant/package.json index c3543742..617d6395 100644 --- a/packages/vector-qdrant/package.json +++ b/packages/vector-qdrant/package.json @@ -1,6 +1,6 @@ { "name": "@anvia/qdrant", - "version": "0.2.6", + "version": "0.2.7", "description": "Qdrant vector store adapter for Anvia.", "author": "anvia", "maintainer": "Indra Zulfi", From e09746c1e763acb18ba7b455b8776271b7b9ef1a Mon Sep 17 00:00:00 2001 From: Indra Zulfi Mushoddaq <114550938+indrazm@users.noreply.github.com> Date: Wed, 17 Jun 2026 22:25:16 +0700 Subject: [PATCH 11/16] feat(studio): add multi-provider model config (#112) * feat(studio): add multi-provider model config * feat(studio): show assistant loading indicator * chore: add studio multi-provider changeset * docs(studio): document model config types * docs(studio): add model provider config guide --- .changeset/studio-multi-provider-models.md | 5 + .../content/docs/reference/api-coverage.mdx | 2 +- .../content/docs/reference/studio/types.mdx | 93 ++++ .../content/docs/studio/configure/meta.json | 9 +- .../docs/studio/configure/model-providers.mdx | 160 ++++++ .../09_studio/13-multi-provider-models.ts | 99 ++++ examples/cookbook/README.md | 4 +- examples/cookbook/package.json | 1 + package.json | 1 + packages/tool-studio/README.md | 62 +++ packages/tool-studio/src/runtime/models.ts | 468 ++++++++++++++++++ packages/tool-studio/src/runtime/runs.ts | 64 +++ packages/tool-studio/src/runtime/shared.ts | 8 + packages/tool-studio/src/runtime/studio.ts | 80 ++- .../tool-studio/src/runtime/transcript.ts | 50 +- .../tool-studio/src/storage/memory-store.ts | 14 + .../tool-studio/src/storage/sqlite-store.ts | 21 + packages/tool-studio/src/types.ts | 92 ++++ packages/tool-studio/src/ui/app/app.tsx | 387 ++++++++++++++- .../modules/playground/transcript-item.tsx | 91 +++- .../src/ui/app/modules/shared/types.ts | 13 +- packages/tool-studio/test/runner.test.ts | 222 +++++++++ 22 files changed, 1901 insertions(+), 45 deletions(-) create mode 100644 .changeset/studio-multi-provider-models.md create mode 100644 apps/docs/content/docs/studio/configure/model-providers.mdx create mode 100644 examples/cookbook/09_studio/13-multi-provider-models.ts create mode 100644 packages/tool-studio/src/runtime/models.ts diff --git a/.changeset/studio-multi-provider-models.md b/.changeset/studio-multi-provider-models.md new file mode 100644 index 00000000..28078e1f --- /dev/null +++ b/.changeset/studio-multi-provider-models.md @@ -0,0 +1,5 @@ +--- +"@anvia/studio": minor +--- + +Add multi-provider model selection and multimodal attachment support to Studio, including cookbook documentation and assistant loading feedback in the playground. diff --git a/apps/docs/content/docs/reference/api-coverage.mdx b/apps/docs/content/docs/reference/api-coverage.mdx index 4a709c45..aca28dde 100644 --- a/apps/docs/content/docs/reference/api-coverage.mdx +++ b/apps/docs/content/docs/reference/api-coverage.mdx @@ -28,7 +28,7 @@ Internal source-file exports are outside this check unless they are re-exported | `@anvia/qdrant` | 1 | 5 | [Qdrant](/docs/reference/integrations/qdrant) | | `@anvia/langfuse` | 1 | 6 | [Langfuse](/docs/reference/integrations/langfuse) | | `@anvia/otel` | 1 | 3 | [OpenTelemetry](/docs/reference/integrations/otel) | -| `@anvia/studio` | 1 | 61 | [Studio](/docs/reference/studio) | +| `@anvia/studio` | 1 | 128 | [Studio](/docs/reference/studio) | The check must report: diff --git a/apps/docs/content/docs/reference/studio/types.mdx b/apps/docs/content/docs/reference/studio/types.mdx index 18e1ce21..886baff3 100644 --- a/apps/docs/content/docs/reference/studio/types.mdx +++ b/apps/docs/content/docs/reference/studio/types.mdx @@ -95,7 +95,9 @@ type StudioConfig = { description?: string; version?: string; agents: StudioAgentConfig[]; + models?: StudioModelsConfig; pipelines: StudioPipelineConfig[]; + evals: StudioEvalSuiteConfig[]; chat: { quickPrompts: Record }; capabilities: Partial>; unsupportedCapabilities: StudioCapability[]; @@ -108,6 +110,88 @@ Return behavior: `Studio.config()` returns `StudioConfig`. Pipelines are top-lev Notable errors: none directly. +## Model Configuration Types + +```ts +type StudioModelRef = string | { provider: string; model: string }; + +type StudioModelModality = "text" | "image" | "document" | "audio" | "video"; + +type StudioModelModalities = { + input: StudioModelModality[]; + output?: StudioModelModality[]; +}; + +type StudioModelDefinition = { + id: string; + name?: string; + description?: string; + modalities?: StudioModelModalities; + capabilities?: Partial; + metadata?: JsonObject; +}; + +type StudioModelProvider = { + id: string; + name?: string; + defaultModel?: string; + models?: StudioModelDefinition[]; + createCompletionModel(model: string): CompletionModel | StreamingCompletionModel; + listModels?: () => Promise; + metadata?: JsonObject; +}; + +type StudioAgentModelPolicy = { + default?: StudioModelRef; + allowed?: Array; +}; + +type StudioModelConfig = { + providers: StudioModelProvider[]; + default?: StudioModelRef; + agents?: Record; +}; + +type StudioModelSummary = StudioModelDefinition & { + ref: string; + providerId: string; + providerName?: string; +}; + +type StudioModelProviderConfig = { + id: string; + name?: string; + defaultModel?: string; + models: StudioModelSummary[]; + metadata?: JsonObject; + warning?: string; +}; + +type StudioAgentModelPolicyConfig = { + default?: string; + allowed?: string[]; +}; + +type StudioModelsConfig = { + providers: StudioModelProviderConfig[]; + default?: string; + agents: Record; +}; + +type StudioAgentModelsSummary = { + agentId: string; + defaultModel?: string; + models: StudioModelSummary[]; + warnings?: JsonObject[]; +}; +``` + +Purpose: configure the provider/model menu Studio exposes for agent runs. `StudioModelProvider` bridges human-facing model metadata to runtime `CompletionModel` instances. `StudioModelModalities` records whether a model accepts text, image, document, audio, or video input and which output modalities it can produce. + +Return behavior: `StudioModelConfig` is accepted through `new Studio(targets, { models })`. `StudioModelsConfig` appears on `GET /config`; `StudioAgentModelsSummary` appears on `GET /agents/:agentId/models`; `StudioModelSummary` values use canonical `provider:model` refs for UI selection and HTTP run requests. + +Notable errors: run requests that select a model outside an agent's `StudioAgentModelPolicy` return `bad_request`. Unsupported modality selections are reported as warnings when metadata is available. + ## Tool Metadata Types ```ts @@ -168,6 +252,14 @@ type StudioAgentMcpsSummary = { agentId: string; servers: StudioAgentMcpServerMetadata[]; }; + +type StudioTranscriptAttachment = { + kind: "image" | "document"; + name?: string; + mediaType?: string; + data?: string; + url?: string; +}; ``` Purpose: metadata returned by `GET /agents/:agentId/tools` and used by the Studio Tools inspector. @@ -288,6 +380,7 @@ type AgentRunRequest = { message: string | Message; history?: Message[]; sessionId?: string; + model?: StudioModelRef; stream?: boolean; maxTurns?: number; toolConcurrency?: number; diff --git a/apps/docs/content/docs/studio/configure/meta.json b/apps/docs/content/docs/studio/configure/meta.json index 5ecd211b..cf0c0141 100644 --- a/apps/docs/content/docs/studio/configure/meta.json +++ b/apps/docs/content/docs/studio/configure/meta.json @@ -2,5 +2,12 @@ "title": "Configure Studio", "defaultOpen": false, "collapsible": true, - "pages": ["serve-options", "quick-prompts", "storage-and-persistence", "capabilities", "status"] + "pages": [ + "serve-options", + "quick-prompts", + "model-providers", + "storage-and-persistence", + "capabilities", + "status" + ] } diff --git a/apps/docs/content/docs/studio/configure/model-providers.mdx b/apps/docs/content/docs/studio/configure/model-providers.mdx new file mode 100644 index 00000000..f24b8731 --- /dev/null +++ b/apps/docs/content/docs/studio/configure/model-providers.mdx @@ -0,0 +1,160 @@ +--- +title: Model Providers +description: Configure Studio to select models across providers per run. +--- + +Studio can expose a model selector for each agent. Use it when you want the same agent instructions, tools, sessions, and traces to run against multiple provider models. + +The agent still needs a normal default model at build time. The Studio `models` option adds runtime model choices for Studio runs. + +```ts +import { AnthropicClient } from "@anvia/anthropic"; +import { AgentBuilder } from "@anvia/core/agent"; +import { OpenAIClient } from "@anvia/openai"; +import { Studio } from "@anvia/studio"; + +const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY }); +const anthropic = new AnthropicClient({ apiKey: process.env.ANTHROPIC_API_KEY }); + +const agent = new AgentBuilder("assistant", openai.completionModel("gpt-5.5")) + .name("Assistant") + .instructions("You are a helpful general-purpose assistant.") + .build(); + +new Studio([agent], { + models: { + default: "openai:gpt-5.5", + providers: [ + { + id: "openai", + name: "OpenAI", + defaultModel: "gpt-5.5", + createCompletionModel: (model) => openai.completionModel(model), + listModels: () => openai.listModels(), + models: [ + { + id: "gpt-5.5", + name: "GPT-5.5", + modalities: { + input: ["text", "image", "document"], + output: ["text"], + }, + }, + ], + }, + { + id: "anthropic", + name: "Anthropic", + defaultModel: "claude-opus-4-8", + createCompletionModel: (model) => anthropic.completionModel(model), + listModels: () => anthropic.listModels(), + models: [ + { + id: "claude-opus-4-8", + name: "Claude Opus 4.8", + modalities: { + input: ["text", "image", "document"], + output: ["text"], + }, + }, + ], + }, + ], + agents: { + assistant: { + default: "openai:gpt-5.5", + allowed: ["openai:gpt-5.5", "anthropic:claude-opus-4-8"], + }, + }, + }, +}).start({ port: 4021 }); +``` + +## Provider Config + +Each provider entry describes how Studio should create a runtime model from a selected model id. + +| Field | Purpose | +| --- | --- | +| `id` | Stable provider id used in model refs, such as `openai` | +| `name` | Human label shown in Studio | +| `defaultModel` | Provider-local fallback model id | +| `createCompletionModel` | Factory that returns a `CompletionModel` or `StreamingCompletionModel` | +| `listModels` | Optional dynamic model catalog from the provider | +| `models` | Optional static metadata for labels, modalities, capabilities, and custom metadata | + +Static `models` metadata is useful when the provider's model list does not include all the display or modality information you want Studio to show. + +## Model Refs + +Studio identifies configured models with `provider:model` refs. + +```ts +models: { + default: "openai:gpt-5.5", + agents: { + assistant: { + default: "anthropic:claude-opus-4-8", + allowed: ["openai:*", "anthropic:claude-opus-4-8"], + }, + }, +} +``` + +Use an object ref when constructing config programmatically: + +```ts +default: { provider: "openai", model: "gpt-5.5" } +``` + +The `allowed` list can include exact refs or provider wildcards such as `openai:*`. + +## Multimodal Metadata + +Use `modalities` to tell Studio which file inputs a model can accept. + +```ts +{ + id: "gpt-5.5", + modalities: { + input: ["text", "image", "document"], + output: ["text"], + }, + capabilities: { + streaming: true, + tools: true, + imageInput: true, + documentInput: true, + }, +} +``` + +When a user attaches images or documents, Studio sends a structured `Message` to the selected model. Attachments are read in the browser and sent with the run request; Studio does not upload them to a standalone file store. + +## HTTP API + +Inspect the resolved Studio-wide model config: + +```bash +curl http://localhost:4021/models +``` + +Inspect the model choices allowed for one agent: + +```bash +curl http://localhost:4021/agents/assistant/models +``` + +Run an agent with a selected model: + +```bash +curl -X POST http://localhost:4021/agents/assistant/runs \ + -H 'content-type: application/json' \ + -d '{"message":"Draft a short project update.","model":"anthropic:claude-opus-4-8"}' +``` + +Studio persists the selected model ref in session metadata so continued sessions can keep using the same model unless the user changes it. + +## Cookbook + +See `examples/cookbook/09_studio/13-multi-provider-models.ts` for a complete runnable OpenAI and Anthropic setup. diff --git a/examples/cookbook/09_studio/13-multi-provider-models.ts b/examples/cookbook/09_studio/13-multi-provider-models.ts new file mode 100644 index 00000000..77eff90a --- /dev/null +++ b/examples/cookbook/09_studio/13-multi-provider-models.ts @@ -0,0 +1,99 @@ +import { AnthropicClient } from "@anvia/anthropic"; +import { AgentBuilder } from "@anvia/core/agent"; +import { OpenAIClient } from "@anvia/openai"; +import { Studio } from "@anvia/studio"; + +const openai = new OpenAIClient({ + baseUrl: process.env.OPENAI_BASEURL, + apiKey: process.env.OPENAI_API_KEY, +}); + +const anthropic = new AnthropicClient({ + baseUrl: process.env.ANTHROPIC_BASEURL, + apiKey: process.env.ANTHROPIC_API_KEY, +}); + +const agent = new AgentBuilder("studio-model-router", openai.completionModel("gpt-5.5")) + .name("Studio Model Router") + .description("Demonstrates Studio model selection across multiple providers.") + .instructions( + [ + "You are a helpful general-purpose assistant used to compare provider behavior in Anvia Studio.", + "Answer clearly and concisely. Adapt your tone and depth to the user's request.", + ].join("\n"), + ) + .build(); + +new Studio([agent], { + models: { + default: "openai:gpt-5.5", + providers: [ + { + id: "openai", + name: "OpenAI", + defaultModel: "gpt-5.5", + createCompletionModel: (model) => openai.completionModel(model), + listModels: () => openai.listModels(), + models: [ + { + id: "gpt-5.5", + name: "GPT-5.5", + modalities: { + input: ["text", "image", "document"], + output: ["text"], + }, + capabilities: { + streaming: true, + tools: true, + imageInput: true, + documentInput: true, + outputSchema: true, + reasoning: true, + }, + }, + ], + }, + { + id: "anthropic", + name: "Anthropic", + defaultModel: "claude-opus-4-8", + createCompletionModel: (model) => anthropic.completionModel(model), + listModels: () => anthropic.listModels(), + models: [ + { + id: "claude-opus-4-8", + name: "Claude Sonnet 4", + modalities: { + input: ["text", "image", "document"], + output: ["text"], + }, + capabilities: { + streaming: true, + tools: true, + imageInput: true, + documentInput: true, + reasoning: true, + }, + }, + ], + }, + ], + agents: { + "studio-model-router": { + default: "openai:gpt-5.5", + allowed: ["openai:gpt-5.5", "anthropic:claude-opus-4-8"], + }, + }, + }, + quickPrompts: { + "studio-model-router": [ + "Explain the difference between latency, quality, and cost when choosing a model.", + "Draft a short project update for a weekly team check-in.", + "What should I consider before routing multimodal work to a model?", + ], + }, +}).start({ port: 4021 }); + +console.log("Open http://localhost:4021/ui/playground"); +console.log("Use the model selector in the message composer to switch providers per run."); +console.log("Open http://localhost:4021/agents/studio-model-router/models for the model catalog."); diff --git a/examples/cookbook/README.md b/examples/cookbook/README.md index b133e104..fee4734b 100644 --- a/examples/cookbook/README.md +++ b/examples/cookbook/README.md @@ -28,7 +28,7 @@ Legacy script names such as `cookbook:basic:01`, `cookbook:intermediate:14`, `co | `06_retrieval` | Embeddings, in-memory search, metadata filters, RAG context, document loaders, vector stores, and embedding provider variants. | | `07_multi_agent` | Basic agent-tools, pipeline-backed parallel specialists, streaming agent-tools, and event stores. | | `08_evals` | Deterministic metrics, semantic similarity, custom metrics, agent eval targets, and LLM judge/score. | -| `09_studio` | Single-agent, multi-agent, pipeline, eval, and subagent Studio runners, pipeline replay, realtime observability, tool approvals, human feedback, Knowledge, Memory, Status, tool inspection, SQLite persistence, and UI route options. | +| `09_studio` | Single-agent, multi-agent, pipeline, eval, and subagent Studio runners, pipeline replay, realtime observability, tool approvals, human feedback, Knowledge, Memory, Status, tool inspection, SQLite persistence, multi-provider model selection, and UI route options. | | `10_integrations` | MCP tools, local skills, Langfuse tracing, logging, and eval reporting. | ## Environment @@ -70,7 +70,7 @@ Not every example needs every variable. Pure pipeline, dynamic tool, and core ev - `retrieval:13` uses a hosted Pinecone index. Create a 384-dimension index first, then set `PINECONE_API_KEY`, `PINECONE_INDEX_NAME`, and optionally `PINECONE_NAMESPACE`. - Langfuse examples need Langfuse credentials and live in `10_integrations`. - `integrations:06` logs agent lifecycle events with Pino through `@anvia/logger`; `integrations:07` shows the built-in console logger. -- Studio examples start a local HTTP server and keep Studio state in memory by default. `studio:10` shows explicit SQLite store wiring for sessions, traces, pipeline logs, and pipeline run history. +- Studio examples start a local HTTP server and keep Studio state in memory by default. `studio:10` shows explicit SQLite store wiring for sessions, traces, pipeline logs, and pipeline run history. `studio:13` shows the Studio message-composer model selector across OpenAI and Anthropic. - Tool history and loader examples write sample files under `.memory`. - Image and audio generation examples write generated media files in the current working directory. - `providers:09` uses the bundled `assets/audio/voice.wav` sample by default. Set `ANVIA_AUDIO_FILE` to transcribe a different local audio file. diff --git a/examples/cookbook/package.json b/examples/cookbook/package.json index d2402ba0..92c6d852 100644 --- a/examples/cookbook/package.json +++ b/examples/cookbook/package.json @@ -90,6 +90,7 @@ "studio:10": "tsx -r dotenv/config 09_studio/10-persistent-store.ts dotenv_config_path=../../.env", "studio:11": "tsx -r dotenv/config 09_studio/11-eval-runner.ts dotenv_config_path=../../.env", "studio:12": "tsx -r dotenv/config 09_studio/12-ui-options.ts dotenv_config_path=../../.env", + "studio:13": "tsx -r dotenv/config 09_studio/13-multi-provider-models.ts dotenv_config_path=../../.env", "integrations": "tsx -r dotenv/config 10_integrations/01-mcp-tools.ts dotenv_config_path=../../.env", "integrations:01": "tsx -r dotenv/config 10_integrations/01-mcp-tools.ts dotenv_config_path=../../.env", "integrations:02": "tsx -r dotenv/config 10_integrations/02-local-skills.ts dotenv_config_path=../../.env", diff --git a/package.json b/package.json index 0753cd2a..c03d83f6 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "cookbook:studio:10": "pnpm --filter cookbook studio:10", "cookbook:studio:11": "pnpm --filter cookbook studio:11", "cookbook:studio:12": "pnpm --filter cookbook studio:12", + "cookbook:studio:13": "pnpm --filter cookbook studio:13", "cookbook:integrations": "pnpm --filter cookbook integrations", "cookbook:integrations:01": "pnpm --filter cookbook integrations:01", "cookbook:integrations:02": "pnpm --filter cookbook integrations:02", diff --git a/packages/tool-studio/README.md b/packages/tool-studio/README.md index 2248b63c..f5d93631 100644 --- a/packages/tool-studio/README.md +++ b/packages/tool-studio/README.md @@ -44,6 +44,68 @@ Then open: http://localhost:4021/ui/playground ``` +## Multi-Provider Models + +Studio can expose a shared model catalog and let each agent choose from registered providers: + +```ts +import { AgentBuilder } from "@anvia/core"; +import { AnthropicClient } from "@anvia/anthropic"; +import { OpenAIClient } from "@anvia/openai"; +import { Studio } from "@anvia/studio"; + +const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY }); +const anthropic = new AnthropicClient({ apiKey: process.env.ANTHROPIC_API_KEY }); + +const agent = new AgentBuilder("support", openai.completionModel("gpt-5")) + .name("Support") + .instructions("Answer support questions clearly.") + .build(); + +new Studio([agent], { + models: { + providers: [ + { + id: "openai", + name: "OpenAI", + defaultModel: "gpt-5", + createCompletionModel: (model) => openai.completionModel(model), + listModels: () => openai.listModels(), + models: [ + { + id: "gpt-5", + modalities: { input: ["text", "image", "document"], output: ["text"] }, + }, + ], + }, + { + id: "anthropic", + name: "Anthropic", + defaultModel: "claude-sonnet-4-20250514", + createCompletionModel: (model) => anthropic.completionModel(model), + }, + ], + agents: { + support: { + default: "openai:gpt-5", + allowed: ["openai:*", "anthropic:claude-sonnet-4-20250514"], + }, + }, + }, +}).start(); +``` + +The playground message composer shows the allowed models for the selected agent. API callers can +also select a model per run: + +```json +{ + "message": "Summarize this ticket", + "model": "anthropic:claude-sonnet-4-20250514", + "stream": true +} +``` + ## Browser UI Studio exposes: diff --git a/packages/tool-studio/src/runtime/models.ts b/packages/tool-studio/src/runtime/models.ts new file mode 100644 index 00000000..ce64e53f --- /dev/null +++ b/packages/tool-studio/src/runtime/models.ts @@ -0,0 +1,468 @@ +import type { CompletionModel, JsonObject, Message } from "@anvia/core/completion"; +import type { Hono } from "hono"; +import type { + AgentRunRequest, + StudioAgent, + StudioAgentModelPolicy, + StudioAgentModelPolicyConfig, + StudioAgentModelsSummary, + StudioModelConfig, + StudioModelDefinition, + StudioModelModality, + StudioModelProvider, + StudioModelProviderConfig, + StudioModelRef, + StudioModelSummary, + StudioModelsConfig, +} from "../types"; +import { errorResponse, serializeError } from "./shared"; + +export const STUDIO_MODEL_METADATA_KEY = "studioModel"; + +type RuntimeProvider = StudioModelProvider & { + staticModels: Map; +}; + +export type StudioModelRegistry = { + readonly defaultModel?: string; + readonly providers: Map; + readonly agentPolicies: Map; + readonly modelCache: Map; +}; + +type NormalizedAgentModelPolicy = { + default?: string; + allowed?: string[]; +}; + +type ResolveModelInput = { + agent: StudioAgent; + request: AgentRunRequest; + sessionMetadata?: JsonObject | undefined; +}; + +export type StudioResolvedModel = { + ref?: string; + model?: CompletionModel; + warnings: JsonObject[]; +}; + +export function createStudioModelRegistry( + config: StudioModelConfig | undefined, +): StudioModelRegistry | undefined { + if (config === undefined) { + return undefined; + } + + const providers = new Map(); + for (const provider of config.providers) { + const id = normalizeProviderId(provider.id); + if (providers.has(id)) { + throw new Error(`Duplicate Studio model provider id: ${id}`); + } + const staticModels = new Map(); + for (const model of provider.models ?? []) { + const modelId = normalizeModelId(model.id); + if (staticModels.has(modelId)) { + throw new Error(`Duplicate Studio model id for provider ${id}: ${modelId}`); + } + staticModels.set(modelId, { ...model, id: modelId }); + } + providers.set(id, { + ...provider, + id, + ...(provider.defaultModel === undefined + ? {} + : { defaultModel: normalizeModelId(provider.defaultModel) }), + staticModels, + }); + } + + return { + ...(config.default === undefined ? {} : { defaultModel: normalizeModelRef(config.default) }), + providers, + agentPolicies: normalizeAgentPolicies(config.agents ?? {}), + modelCache: new Map(), + }; +} + +export function studioModelsConfig( + registry: StudioModelRegistry | undefined, + agents: StudioAgent[], +): StudioModelsConfig | undefined { + if (registry === undefined) { + return undefined; + } + + const providers = [...registry.providers.values()].map((provider): StudioModelProviderConfig => { + const models = [...provider.staticModels.values()].map((model) => + modelSummary(provider, model.id, model), + ); + return { + id: provider.id, + ...(provider.name === undefined ? {} : { name: provider.name }), + ...(provider.defaultModel === undefined ? {} : { defaultModel: provider.defaultModel }), + models, + ...(provider.metadata === undefined ? {} : { metadata: provider.metadata }), + }; + }); + + const agentIds = new Set(agents.map((agent) => agent.id)); + const agentsConfig = Object.fromEntries( + [...registry.agentPolicies.entries()] + .filter(([agentId]) => agentIds.has(agentId)) + .map(([agentId, policy]) => [agentId, publicPolicy(policy)]), + ); + + return { + providers, + ...(registry.defaultModel === undefined ? {} : { default: registry.defaultModel }), + agents: agentsConfig, + }; +} + +export function registerModelRoutes( + app: Hono, + props: { registry?: StudioModelRegistry | undefined; agentMap: Map }, +): void { + app.get("/models", async (c) => { + if (props.registry === undefined) { + return errorResponse(c, 404, "not_found", "Model registry not configured"); + } + const providers = await Promise.all( + [...props.registry.providers.values()].map((provider) => providerCatalog(provider)), + ); + return c.json({ + providers, + ...(props.registry.defaultModel === undefined + ? {} + : { defaultModel: props.registry.defaultModel }), + }); + }); + + app.get("/models/:providerId", async (c) => { + if (props.registry === undefined) { + return errorResponse(c, 404, "not_found", "Model registry not configured"); + } + const provider = props.registry.providers.get(c.req.param("providerId")); + if (provider === undefined) { + return errorResponse(c, 404, "not_found", "Model provider not found"); + } + return c.json(await providerCatalog(provider)); + }); + + app.get("/agents/:agentId/models", async (c) => { + const agentId = c.req.param("agentId"); + const agent = props.agentMap.get(agentId); + if (agent === undefined) { + return errorResponse(c, 404, "not_found", "Agent not found"); + } + if (props.registry === undefined) { + return c.json({ + agentId, + models: [], + } satisfies StudioAgentModelsSummary); + } + return c.json(await agentModelsCatalog(props.registry, agent)); + }); +} + +export function resolveStudioModel( + registry: StudioModelRegistry | undefined, + input: ResolveModelInput, +): StudioResolvedModel { + if (registry === undefined) { + return { warnings: [] }; + } + + const selectedRef = + normalizeOptionalModelRef(input.request.model) ?? + sessionModelRef(input.sessionMetadata) ?? + registry.agentPolicies.get(input.agent.id)?.default ?? + registry.defaultModel; + if (selectedRef === undefined) { + return { warnings: [] }; + } + + ensureModelAllowed(registry, input.agent.id, selectedRef); + const { providerId, modelId } = parseModelRef(selectedRef); + const provider = registry.providers.get(providerId); + if (provider === undefined) { + throw new ModelSelectionError(`Unknown model provider: ${providerId}`); + } + + let model = registry.modelCache.get(selectedRef); + if (model === undefined) { + model = provider.createCompletionModel(modelId); + registry.modelCache.set(selectedRef, model); + } + + const metadata = provider.staticModels.get(modelId); + return { + ref: selectedRef, + model, + warnings: modelWarnings(selectedRef, metadata, input.request), + }; +} + +export function sessionModelRef(metadata: JsonObject | undefined): string | undefined { + const value = metadata?.[STUDIO_MODEL_METADATA_KEY]; + return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined; +} + +export function normalizeOptionalModelRef(ref: StudioModelRef | undefined): string | undefined { + return ref === undefined ? undefined : normalizeModelRef(ref); +} + +export function normalizeModelRef(ref: StudioModelRef): string { + if (typeof ref === "string") { + const trimmed = ref.trim(); + const parsed = parseModelRef(trimmed); + return `${parsed.providerId}:${parsed.modelId}`; + } + return `${normalizeProviderId(ref.provider)}:${normalizeModelId(ref.model)}`; +} + +export function parseModelRef(ref: string): { providerId: string; modelId: string } { + const index = ref.indexOf(":"); + if (index <= 0 || index === ref.length - 1) { + throw new ModelSelectionError(`Model ref must use provider:model format: ${ref}`); + } + return { + providerId: normalizeProviderId(ref.slice(0, index)), + modelId: normalizeModelId(ref.slice(index + 1)), + }; +} + +export class ModelSelectionError extends Error { + constructor(message: string) { + super(message); + this.name = "ModelSelectionError"; + } +} + +async function agentModelsCatalog( + registry: StudioModelRegistry, + agent: StudioAgent, +): Promise { + const policy = registry.agentPolicies.get(agent.id); + const catalogs = await Promise.all([...registry.providers.values()].map(providerCatalog)); + const warnings = catalogs.flatMap((catalog) => + catalog.warning === undefined + ? [] + : [{ providerId: catalog.id, warning: catalog.warning } satisfies JsonObject], + ); + const models = catalogs + .flatMap((catalog) => catalog.models) + .filter((model) => { + if (policy?.allowed === undefined) { + return true; + } + return allowedByPolicy(policy.allowed, model.ref); + }); + const exactPolicyModels = (policy?.allowed ?? []) + .filter((allowed) => !allowed.endsWith(":*")) + .filter((allowed) => models.every((model) => model.ref !== allowed)) + .flatMap((ref) => { + try { + const { providerId, modelId } = parseModelRef(ref); + const provider = registry.providers.get(providerId); + return provider === undefined + ? [] + : [ + modelSummary(provider, modelId, { + id: modelId, + }), + ]; + } catch { + return []; + } + }); + + const defaultModel = policy?.default ?? registry.defaultModel; + return { + agentId: agent.id, + ...(defaultModel === undefined ? {} : { defaultModel }), + models: [...models, ...exactPolicyModels], + ...(warnings.length === 0 ? {} : { warnings }), + }; +} + +async function providerCatalog(provider: RuntimeProvider): Promise { + const models = new Map(); + for (const model of provider.staticModels.values()) { + models.set(model.id, modelSummary(provider, model.id, model)); + } + + let warning: string | undefined; + if (provider.listModels !== undefined) { + try { + const listed = await provider.listModels(); + for (const model of listed.data) { + const staticModel = provider.staticModels.get(model.id); + models.set( + model.id, + modelSummary(provider, model.id, { + id: model.id, + ...(model.name === undefined ? {} : { name: model.name }), + ...(model.description === undefined ? {} : { description: model.description }), + ...(staticModel ?? {}), + metadata: { + ...(model.type === undefined ? {} : { type: model.type }), + ...(model.createdAt === undefined ? {} : { createdAt: model.createdAt }), + ...(model.ownedBy === undefined ? {} : { ownedBy: model.ownedBy }), + ...(model.contextLength === undefined ? {} : { contextLength: model.contextLength }), + ...(staticModel?.metadata ?? {}), + }, + }), + ); + } + } catch (error) { + const serialized = serializeError(error); + warning = + typeof serialized === "object" && serialized !== null && "message" in serialized + ? String(serialized.message) + : String(error); + } + } + + return { + id: provider.id, + ...(provider.name === undefined ? {} : { name: provider.name }), + ...(provider.defaultModel === undefined ? {} : { defaultModel: provider.defaultModel }), + models: [...models.values()].sort((left, right) => left.ref.localeCompare(right.ref)), + ...(provider.metadata === undefined ? {} : { metadata: provider.metadata }), + ...(warning === undefined ? {} : { warning }), + }; +} + +function modelSummary( + provider: RuntimeProvider, + modelId: string, + model: StudioModelDefinition, +): StudioModelSummary { + return { + ...model, + id: modelId, + ref: `${provider.id}:${modelId}`, + providerId: provider.id, + ...(provider.name === undefined ? {} : { providerName: provider.name }), + }; +} + +function ensureModelAllowed(registry: StudioModelRegistry, agentId: string, ref: string): void { + const { providerId } = parseModelRef(ref); + if (!registry.providers.has(providerId)) { + throw new ModelSelectionError(`Unknown model provider: ${providerId}`); + } + const policy = registry.agentPolicies.get(agentId); + if (policy?.allowed !== undefined && !allowedByPolicy(policy.allowed, ref)) { + throw new ModelSelectionError(`Model ${ref} is not allowed for agent ${agentId}`); + } +} + +function allowedByPolicy(allowed: string[], ref: string): boolean { + return allowed.some((entry) => + entry.endsWith(":*") ? ref.startsWith(entry.slice(0, -1)) : entry === ref, + ); +} + +function normalizeAgentPolicies( + policies: Record, +): Map { + return new Map( + Object.entries(policies).map(([agentId, policy]) => [ + agentId.trim(), + { + ...(policy.default === undefined ? {} : { default: normalizeModelRef(policy.default) }), + ...(policy.allowed === undefined + ? {} + : { + allowed: policy.allowed.map((entry) => + typeof entry === "string" && entry.trim().endsWith(":*") + ? `${normalizeProviderId(entry.trim().slice(0, -2))}:*` + : normalizeModelRef(entry), + ), + }), + }, + ]), + ); +} + +function publicPolicy(policy: NormalizedAgentModelPolicy): StudioAgentModelPolicyConfig { + return { + ...(policy.default === undefined ? {} : { default: policy.default }), + ...(policy.allowed === undefined ? {} : { allowed: policy.allowed }), + }; +} + +function modelWarnings( + ref: string, + model: StudioModelDefinition | undefined, + request: AgentRunRequest, +): JsonObject[] { + const warnings: JsonObject[] = []; + const modalities = requestModalities(request); + const missingModalities = [...modalities].filter( + (modality) => !model?.modalities?.input.includes(modality), + ); + if (model?.modalities !== undefined && missingModalities.length > 0) { + warnings.push({ + model: ref, + kind: "modality", + message: `Model ${ref} does not declare input support for ${missingModalities.join(", ")}`, + missing: missingModalities, + }); + } + + if (request.stream === true && model?.capabilities?.streaming === false) { + warnings.push({ + model: ref, + kind: "capability", + message: `Model ${ref} is configured as non-streaming but the run requested streaming`, + capability: "streaming", + }); + } + + return warnings; +} + +function requestModalities(request: AgentRunRequest): Set { + const modalities = new Set(["text"]); + for (const message of requestMessages(request)) { + if (typeof message === "string") { + continue; + } + if (message.role === "user" || message.role === "assistant") { + for (const content of message.content) { + if (content.type === "image") { + modalities.add("image"); + } + if (content.type === "document") { + modalities.add("document"); + } + } + } + } + return modalities; +} + +function requestMessages(request: AgentRunRequest): Array { + return [...(request.history ?? []), request.message]; +} + +function normalizeProviderId(id: string): string { + const trimmed = id.trim(); + if (trimmed.length === 0 || trimmed.includes(":")) { + throw new ModelSelectionError("Model provider id must be non-empty and cannot contain ':'"); + } + return trimmed; +} + +function normalizeModelId(id: string): string { + const trimmed = id.trim(); + if (trimmed.length === 0) { + throw new ModelSelectionError("Model id cannot be empty"); + } + return trimmed; +} diff --git a/packages/tool-studio/src/runtime/runs.ts b/packages/tool-studio/src/runtime/runs.ts index a500e4bb..9ac126ba 100644 --- a/packages/tool-studio/src/runtime/runs.ts +++ b/packages/tool-studio/src/runtime/runs.ts @@ -7,6 +7,7 @@ import type { AgentRunStreamEvent, StudioSession, StudioSessionStore, + StudioTranscriptAttachment, StudioTranscriptChildAgentEvent, StudioTranscriptEntry, } from "../types"; @@ -476,6 +477,7 @@ function messageToTranscriptEntry( kind: "message", role, text: extractMessageText(message), + ...(role === "user" ? optionalTranscriptAttachments(message) : {}), }; } @@ -595,6 +597,44 @@ function extractMessageText(message: string | Message): string { .join("\n"); } +function optionalTranscriptAttachments(message: string | Message): { + attachments?: StudioTranscriptAttachment[]; +} { + if (typeof message === "string" || message.role !== "user") { + return {}; + } + const attachments = message.content.flatMap((content): StudioTranscriptAttachment[] => { + if (content.type === "image") { + return [ + { + kind: "image", + ...(content.source.type === "base64" + ? { data: content.source.data, mediaType: content.source.mediaType } + : { url: content.source.url }), + }, + ]; + } + if (content.type === "document") { + return [ + { + kind: "document", + ...(content.source.filename === undefined ? {} : { name: content.source.filename }), + ...(content.source.mediaType === undefined + ? {} + : { mediaType: content.source.mediaType }), + ...(content.source.type === "base64" + ? { data: content.source.data } + : content.source.type === "url" + ? { url: content.source.url } + : {}), + }, + ]; + } + return []; + }); + return attachments.length === 0 ? {} : { attachments }; +} + function formatJson(value: unknown): string { try { return JSON.stringify(value, null, 2); @@ -669,6 +709,30 @@ export async function parseRunRequest(c: Context): Promise withStudioTraceObserver(agent, stores.traces), ); + const modelRegistry = createStudioModelRegistry(options.models); const pipelines = normalizePipelines(options.pipelines); const agentMap = new Map(agents.map((agent) => [agent.id, agent])); const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline])); @@ -287,6 +297,7 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { return c.json(agentRuntimeSummary(agent)); }); + registerModelRoutes(app, { registry: modelRegistry, agentMap }); registerMcpRoutes(app, { agentMap }); registerToolRoutes(app, { agentMap }); registerApprovalRoutes(app, approvalRuntime); @@ -332,6 +343,24 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { return errorResponse(c, 400, "bad_request", "Session belongs to another agent"); } + let selectedModel: ReturnType; + try { + selectedModel = resolveStudioModel(modelRegistry, { + agent, + request: body, + sessionMetadata: session?.metadata, + }); + } catch (error) { + if (error instanceof ModelSelectionError) { + return errorResponse(c, 400, "bad_request", error.message); + } + throw error; + } + const runAgent = + selectedModel.model === undefined + ? agent.agent + : cloneAgent(agent.agent, { model: selectedModel.model }); + const runId = globalThis.crypto.randomUUID(); const runStartedAt = Date.now(); if (session !== undefined) { @@ -346,13 +375,44 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { ...(body.maxTurns === undefined ? {} : { maxTurns: body.maxTurns }), ...(body.toolConcurrency === undefined ? {} : { toolConcurrency: body.toolConcurrency }), hasTrace: body.trace !== undefined, - ...(body.metadata === undefined ? {} : { metadata: body.metadata }), + ...(body.metadata === undefined && selectedModel.ref === undefined + ? {} + : { + metadata: { + ...(body.metadata ?? {}), + ...(selectedModel.ref === undefined + ? {} + : { [STUDIO_MODEL_METADATA_KEY]: selectedModel.ref }), + }, + }), }), ); } + if (session !== undefined && selectedModel.ref !== undefined) { + for (const warning of selectedModel.warnings) { + await appendSessionLog(stores.sessions, { + sessionId: session.id, + runId, + level: "warn", + category: "model", + event: "model.warning", + message: typeof warning.message === "string" ? warning.message : "Model warning", + metadata: warning, + }); + } + if (sessionModelRef(session.metadata) !== selectedModel.ref) { + await stores.sessions?.updateSessionMetadata?.(session.id, { + ...(session.metadata ?? {}), + [STUDIO_MODEL_METADATA_KEY]: selectedModel.ref, + }); + } + } const memoryMetadata = { agentId, ...(body.metadata ?? {}), + ...(selectedModel.ref === undefined + ? {} + : { [STUDIO_MODEL_METADATA_KEY]: selectedModel.ref }), studioRunId: runId, }; const promptMessage = normalizePromptMessage(body.message); @@ -360,7 +420,7 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { const shouldPersistSessionMessages = session !== undefined && sessionStore !== undefined && - !usesStoreAsAgentMemory(agent.agent, sessionStore); + !usesStoreAsAgentMemory(runAgent, sessionStore); if (shouldPersistSessionMessages) { await sessionStore.append({ context: { sessionId: session.id, metadata: memoryMetadata }, @@ -371,10 +431,10 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { } const request = session !== undefined - ? agent.agent.memory === undefined - ? agent.agent.prompt([...session.messages, promptMessage]) - : agent.agent.session(session.id, { metadata: memoryMetadata }).prompt(body.message) - : agent.agent.prompt( + ? runAgent.memory === undefined + ? runAgent.prompt([...session.messages, promptMessage]) + : runAgent.session(session.id, { metadata: memoryMetadata }).prompt(body.message) + : runAgent.prompt( body.history !== undefined ? [...body.history, promptMessage] : body.message, ); if (body.maxTurns !== undefined) { @@ -393,13 +453,13 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { const runtimeEvents = new AsyncEventQueue(); const effectiveHook = composeHooks( composeHooks( - agent.agent.hook, + runAgent.hook, approvalRuntime.createHook({ runId, agentId, ...(session?.id === undefined ? {} : { sessionId: session.id }), ...(body.metadata === undefined ? {} : { metadata: body.metadata }), - getTool: (toolName) => agent.agent.getTool(toolName), + getTool: (toolName) => runAgent.getTool(toolName), emit: (event) => runtimeEvents.push(event), }), ), @@ -442,13 +502,13 @@ function createStudioApp(options: StudioRuntimeOptions): StudioApp { } const effectiveHook = composeHooks( composeHooks( - agent.agent.hook, + runAgent.hook, approvalRuntime.createHook({ runId, agentId, ...(session?.id === undefined ? {} : { sessionId: session.id }), ...(body.metadata === undefined ? {} : { metadata: body.metadata }), - getTool: (toolName) => agent.agent.getTool(toolName), + getTool: (toolName) => runAgent.getTool(toolName), }), ), questionRuntime.createHook({ diff --git a/packages/tool-studio/src/runtime/transcript.ts b/packages/tool-studio/src/runtime/transcript.ts index c233a442..3ad01fa4 100644 --- a/packages/tool-studio/src/runtime/transcript.ts +++ b/packages/tool-studio/src/runtime/transcript.ts @@ -1,5 +1,5 @@ import type { Message } from "@anvia/core/completion"; -import type { StudioTranscriptEntry } from "../types"; +import type { StudioTranscriptAttachment, StudioTranscriptEntry } from "../types"; export function renumberTranscript(entries: StudioTranscriptEntry[]): StudioTranscriptEntry[] { return entries.map((entry, entryId) => ({ ...entry, entryId })); @@ -12,6 +12,8 @@ export function transcriptFromMessages(messages: Message[]): StudioTranscriptEnt continue; } if (message.role === "user") { + const attachments = attachmentsFromMessage(message); + let textEntryAdded = false; for (const content of message.content) { if (content.type === "text") { transcript.push({ @@ -19,9 +21,20 @@ export function transcriptFromMessages(messages: Message[]): StudioTranscriptEnt kind: "message", role: "user", text: content.text, + ...(attachments.length === 0 ? {} : { attachments }), }); + textEntryAdded = true; } } + if (!textEntryAdded && attachments.length > 0) { + transcript.push({ + entryId: transcript.length, + kind: "message", + role: "user", + text: "", + attachments, + }); + } continue; } if (message.role === "tool") { @@ -66,6 +79,41 @@ export function transcriptFromMessages(messages: Message[]): StudioTranscriptEnt return transcript; } +function attachmentsFromMessage(message: Message): StudioTranscriptAttachment[] { + if (message.role !== "user" && message.role !== "assistant") { + return []; + } + return message.content.flatMap((content): StudioTranscriptAttachment[] => { + if (content.type === "image") { + return [ + { + kind: "image", + ...(content.source.type === "base64" + ? { data: content.source.data, mediaType: content.source.mediaType } + : { url: content.source.url }), + }, + ]; + } + if (content.type === "document") { + return [ + { + kind: "document", + ...(content.source.filename === undefined ? {} : { name: content.source.filename }), + ...(content.source.mediaType === undefined + ? {} + : { mediaType: content.source.mediaType }), + ...(content.source.type === "base64" + ? { data: content.source.data } + : content.source.type === "url" + ? { url: content.source.url } + : {}), + }, + ]; + } + return []; + }); +} + function appendAssistantTranscriptText(transcript: StudioTranscriptEntry[], text: string): void { const last = transcript.at(-1); if (last?.kind === "message" && last.role === "assistant") { diff --git a/packages/tool-studio/src/storage/memory-store.ts b/packages/tool-studio/src/storage/memory-store.ts index 2987f448..fc86d434 100644 --- a/packages/tool-studio/src/storage/memory-store.ts +++ b/packages/tool-studio/src/storage/memory-store.ts @@ -79,6 +79,20 @@ class InMemoryStudioStore return session === undefined ? undefined : materializeSession(session); } + updateSessionMetadata(id: string, metadata: JsonObject | undefined): StudioSession | undefined { + const session = this.sessions.get(id); + if (session === undefined) { + return undefined; + } + if (metadata === undefined) { + delete session.metadata; + } else { + session.metadata = metadata; + } + session.updatedAt = new Date().toISOString(); + return materializeSession(session); + } + load(context: MemoryContext): Promise { return Promise.resolve(this.sessions.get(context.sessionId)?.messages ?? []); } diff --git a/packages/tool-studio/src/storage/sqlite-store.ts b/packages/tool-studio/src/storage/sqlite-store.ts index 652ad857..9396f93c 100644 --- a/packages/tool-studio/src/storage/sqlite-store.ts +++ b/packages/tool-studio/src/storage/sqlite-store.ts @@ -220,6 +220,27 @@ class SqliteSessionStore : toSession(row, this.listSessionMessages(id), this.listSessionRunRows(id)); } + updateSessionMetadata(id: string, metadata: JsonObject | undefined): StudioSession | undefined { + const db = this.database(); + const now = new Date().toISOString(); + const result = db + .prepare( + `UPDATE anvia_studio_sessions + SET metadata_json = $metadata, + updated_at = $now + WHERE id = $id`, + ) + .run({ + $id: id, + $metadata: metadata === undefined ? null : JSON.stringify(metadata), + $now: now, + }); + if (result.changes === 0) { + return undefined; + } + return this.getSession(id); + } + load(context: MemoryContext): Promise { const session = this.getSession(context.sessionId); return Promise.resolve(session?.messages ?? []); diff --git a/packages/tool-studio/src/types.ts b/packages/tool-studio/src/types.ts index db5abbb6..847596d2 100644 --- a/packages/tool-studio/src/types.ts +++ b/packages/tool-studio/src/types.ts @@ -1,14 +1,18 @@ import type { AgentStreamEvent, PromptResponse } from "@anvia/core/agent"; import type { + CompletionModel, + CompletionModelCapabilities, JsonObject, JsonValue, Message, + StreamingCompletionModel, ToolResultContent, Usage, } from "@anvia/core/completion"; import type { RunEvalSuiteOptions } from "@anvia/core/evals"; import type { Agent } from "@anvia/core/internal/agent"; import type { MemoryStore } from "@anvia/core/memory"; +import type { ModelList } from "@anvia/core/model-listing"; import type { AgentTraceInfo, AgentTraceOptions } from "@anvia/core/observability"; import type { Pipeline, PipelineGraph } from "@anvia/core/pipeline"; import type { Hono } from "hono"; @@ -27,6 +31,78 @@ export type StudioCapability = | "tools" | "traces"; +export type StudioModelRef = string | { provider: string; model: string }; + +export type StudioModelModality = "text" | "image" | "document" | "audio" | "video"; + +export type StudioModelModalities = { + input: StudioModelModality[]; + output?: StudioModelModality[]; +}; + +export type StudioModelDefinition = { + id: string; + name?: string; + description?: string; + modalities?: StudioModelModalities; + capabilities?: Partial; + metadata?: JsonObject; +}; + +export type StudioModelProvider = { + id: string; + name?: string; + defaultModel?: string; + models?: StudioModelDefinition[]; + createCompletionModel(model: string): CompletionModel | StreamingCompletionModel; + listModels?: () => Promise; + metadata?: JsonObject; +}; + +export type StudioAgentModelPolicy = { + default?: StudioModelRef; + allowed?: Array; +}; + +export type StudioModelConfig = { + providers: StudioModelProvider[]; + default?: StudioModelRef; + agents?: Record; +}; + +export type StudioModelSummary = StudioModelDefinition & { + ref: string; + providerId: string; + providerName?: string; +}; + +export type StudioModelProviderConfig = { + id: string; + name?: string; + defaultModel?: string; + models: StudioModelSummary[]; + metadata?: JsonObject; + warning?: string; +}; + +export type StudioAgentModelPolicyConfig = { + default?: string; + allowed?: string[]; +}; + +export type StudioModelsConfig = { + providers: StudioModelProviderConfig[]; + default?: string; + agents: Record; +}; + +export type StudioAgentModelsSummary = { + agentId: string; + defaultModel?: string; + models: StudioModelSummary[]; + warnings?: JsonObject[]; +}; + export type StudioAgent = { id: string; agent: Agent; @@ -137,6 +213,7 @@ export type StudioConfig = { description?: string; version?: string; agents: StudioAgentConfig[]; + models?: StudioModelsConfig; pipelines: StudioPipelineConfig[]; evals: StudioEvalSuiteConfig[]; chat: { @@ -211,6 +288,15 @@ export type StudioTranscriptChatEntry = { text: string; tone?: "error"; traceId?: string; + attachments?: StudioTranscriptAttachment[]; +}; + +export type StudioTranscriptAttachment = { + kind: "image" | "document"; + name?: string; + mediaType?: string; + data?: string; + url?: string; }; export type StudioTranscriptReasoningEntry = { @@ -355,6 +441,10 @@ export type StudioSessionStore = MemoryStore & { saveSessionRunTranscript( input: StudioSessionRunTranscriptInput, ): StudioSession | undefined | Promise; + updateSessionMetadata?( + id: string, + metadata: JsonObject | undefined, + ): StudioSession | undefined | Promise; appendSessionLog?( input: StudioSessionLogAppendInput, ): StudioSessionLogEntry | Promise; @@ -605,6 +695,7 @@ export type StudioOptions = { quickPrompts?: Record; stores?: StudioStores; ui?: boolean | StudioUiOptions; + models?: StudioModelConfig; }; export type StudioServeOptions = { @@ -848,6 +939,7 @@ export type AgentRunRequest = { stream?: boolean; maxTurns?: number; toolConcurrency?: number; + model?: StudioModelRef; metadata?: JsonObject; trace?: AgentTraceOptions; }; diff --git a/packages/tool-studio/src/ui/app/app.tsx b/packages/tool-studio/src/ui/app/app.tsx index 1651d398..265295d3 100644 --- a/packages/tool-studio/src/ui/app/app.tsx +++ b/packages/tool-studio/src/ui/app/app.tsx @@ -1,6 +1,15 @@ -import type { Message, ToolResultContent } from "@anvia/core/completion"; +import type { Message, ToolResultContent, UserContent } from "@anvia/core/completion"; import { createChatTransport, EventStreamHttpError, useChat } from "@anvia/react"; -import { Archive, ArrowSquareOut, ArrowUp, Moon, Plus, Sun } from "@phosphor-icons/react"; +import { + Archive, + ArrowSquareOut, + ArrowUp, + Moon, + Paperclip, + Plus, + Sun, + X, +} from "@phosphor-icons/react"; import { type ChangeEvent, type KeyboardEvent, @@ -15,10 +24,12 @@ import { import type { AgentRunStreamEvent, StudioAgentMcpsSummary, + StudioAgentModelsSummary, StudioAgentToolsSummary, StudioConfig, StudioEvalRunResponse, StudioKnowledgeSummary, + StudioModelSummary, StudioPipelineDetail, StudioPipelineLogEntry, StudioPipelineRunRecord, @@ -151,15 +162,29 @@ type StudioTheme = "light" | "dark"; type StudioAgentRunRequest = { agentId: string; - message: string; + message: string | Message; sessionId?: string; history?: Message[]; + model?: string; stream: true; metadata: { source: string; + studioModel?: string; }; }; +const studioModelMetadataKey = "studioModel"; +const supportedAttachmentTypes = ".png,.jpg,.jpeg,.webp,.gif,.pdf,.txt,.md,.csv,.json"; + +type PromptAttachment = { + id: string; + name: string; + mediaType: string; + kind: "image" | "document"; + data: string; + size: number; +}; + const studioThemeStorageKey = "anvia-studio-theme"; function readInitialStudioTheme(): StudioTheme { @@ -251,6 +276,7 @@ export function StudioConsole() { const [pipelineRuns, setPipelineRuns] = useState([]); const [messages, setMessages] = useState([]); const [prompt, setPrompt] = useState(""); + const [attachments, setAttachments] = useState([]); const [pipelineRunInput, setPipelineRunInput] = useState('"Hello from Studio"'); const [pipelineRunOutput, setPipelineRunOutput] = useState(""); const [evalRunResult, setEvalRunResult] = useState(); @@ -279,6 +305,8 @@ export function StudioConsole() { const [mcpsLoadState, setMcpsLoadState] = useState<"idle" | "loading">("idle"); const [tools, setTools] = useState(); const [toolsLoadState, setToolsLoadState] = useState<"idle" | "loading">("idle"); + const [agentModels, setAgentModels] = useState(); + const [selectedModelRef, setSelectedModelRef] = useState(""); const [pipelineDetailLoadState, setPipelineDetailLoadState] = useState<"idle" | "loading">( "idle", ); @@ -287,6 +315,7 @@ export function StudioConsole() { const [pipelineRunState, setPipelineRunState] = useState("idle"); const [evalRunState, setEvalRunState] = useState("idle"); const promptRef = useRef(null); + const attachmentInputRef = useRef(null); const transcriptScrollerRef = useRef(null); const transcriptStickToBottomRef = useRef(true); const playgroundRunRequestRef = useRef(undefined); @@ -392,8 +421,53 @@ export function StudioConsole() { ); const selectedAgent = agents.find((agent) => agent.id === selectedAgentId) ?? agents[0] ?? undefined; + const selectedAgentModelId = selectedAgent?.id; const selectedAgentQuickPrompts = selectedAgent?.quickPrompts ?? []; + const selectedAgentModels = + agentModels !== undefined && agentModels.agentId === selectedAgent?.id + ? agentModels.models + : []; const hasMessages = messages.length > 0; + + useEffect(() => { + if (config?.models === undefined || selectedAgentModelId === undefined) { + setAgentModels(undefined); + setSelectedModelRef(""); + return; + } + + const agentId = selectedAgentModelId; + let cancelled = false; + async function loadAgentModels() { + try { + const response = await fetch(`/agents/${encodeURIComponent(agentId)}/models`); + if (!response.ok) { + throw new Error(`Agent models failed with HTTP ${response.status}`); + } + const body = (await response.json()) as StudioAgentModelsSummary; + if (cancelled) { + return; + } + setAgentModels(body); + setSelectedModelRef((current) => + modelRefAvailable(body.models, current) + ? current + : (body.defaultModel ?? body.models[0]?.ref ?? ""), + ); + } catch (loadError) { + if (!cancelled) { + setAgentModels(undefined); + setError(errorMessage(loadError)); + } + } + } + + void loadAgentModels(); + return () => { + cancelled = true; + }; + }, [config?.models, selectedAgentModelId]); + const playgroundChat = useChat({ transport: createChatTransport({ endpoint: (request) => `/agents/${encodeURIComponent(request.agentId)}/runs`, @@ -502,6 +576,7 @@ export function StudioConsole() { title, metadata: { source: "anvia-studio", + ...(selectedModelRef.length === 0 ? {} : { [studioModelMetadataKey]: selectedModelRef }), }, }), }); @@ -830,8 +905,10 @@ export function StudioConsole() { ]); setTranscriptSequence(nextSequence(session.transcript)); setSelectedAgentId(session.agentId); + setSelectedModelRef(sessionModelRef(session)); setSelectedSessionId(session.id); setMessages(enrichTranscriptWithTraceIds(session.transcript, traceSummaries)); + setAttachments([]); if (options.updatePath !== false) { setActivePage("playground"); updateSessionPath(session.id); @@ -856,6 +933,7 @@ export function StudioConsole() { setSessionLogs([]); setMessages([]); setPrompt(""); + setAttachments([]); setActivePage("playground"); setError(""); if (options.updatePath !== false) { @@ -873,11 +951,13 @@ export function StudioConsole() { } setSelectedAgentId(agentId); + setSelectedModelRef(""); resetTranscriptSequence(); setSelectedSessionId(""); setSessionLogs([]); setMessages([]); setPrompt(""); + setAttachments([]); setActivePage("playground"); setError(""); updateSessionPath(undefined); @@ -908,6 +988,7 @@ export function StudioConsole() { setSessionLogs([]); setMessages([]); setPrompt(""); + setAttachments([]); if (activePage === "playground") { updateSessionPath(undefined); } @@ -971,9 +1052,10 @@ export function StudioConsole() { async function runPrompt(text: string) { const trimmed = text.trim(); + const promptAttachments = attachments; const agentId = selectedAgent?.id ?? selectedAgentId; if ( - trimmed.length === 0 || + (trimmed.length === 0 && promptAttachments.length === 0) || agentId.length === 0 || runState === "running" || playgroundChat.status === "streaming" @@ -985,11 +1067,31 @@ export function StudioConsole() { setActivePage("playground"); setError(""); setPrompt(""); + setAttachments([]); transcriptStickToBottomRef.current = true; requestAnimationFrame(() => resizeTextarea(promptRef.current)); + const promptMessage = + promptAttachments.length === 0 + ? trimmed + : userMessageWithAttachments(trimmed, promptAttachments); setMessages((current) => [ ...current, - { entryId: nextTranscriptId(), kind: "message", role: "user", text: trimmed }, + { + entryId: nextTranscriptId(), + kind: "message", + role: "user", + text: trimmed, + ...(promptAttachments.length === 0 + ? {} + : { attachments: transcriptAttachmentsForPrompt(promptAttachments) }), + }, + { + entryId: nextTranscriptId(), + kind: "message", + role: "assistant", + text: "", + tone: "pending", + }, ]); try { @@ -1002,12 +1104,14 @@ export function StudioConsole() { playgroundVisibleEventRef.current = Promise.resolve(); playgroundRunRequestRef.current = { agentId, - message: trimmed, + message: promptMessage, ...(sessionId.length === 0 ? {} : { sessionId }), ...(history === undefined ? {} : { history }), + ...(selectedModelRef.length === 0 ? {} : { model: selectedModelRef }), stream: true, metadata: { source: "anvia-studio", + ...(selectedModelRef.length === 0 ? {} : { studioModel: selectedModelRef }), }, }; @@ -1228,8 +1332,11 @@ export function StudioConsole() { appendSessionLogEntry(event.log); return true; } - if (event.type === "final" && event.trace?.traceId !== undefined) { - assignAssistantTraceId(event.trace.traceId); + if (event.type === "final") { + if (event.trace?.traceId !== undefined) { + assignAssistantTraceId(event.trace.traceId); + } + clearPendingAssistant(); return true; } if (event.type === "error") { @@ -1265,7 +1372,12 @@ export function StudioConsole() { const next = [...current]; const last = next.at(-1); if (last?.kind === "message" && last.role === "assistant") { - next[next.length - 1] = { ...last, text: `${last.text}${delta}` }; + if (last.tone === "pending") { + const { tone: _tone, ...readyMessage } = last; + next[next.length - 1] = { ...readyMessage, text: delta }; + } else { + next[next.length - 1] = { ...last, text: `${last.text}${delta}` }; + } } else { next.push({ entryId: nextTranscriptId(), @@ -1279,16 +1391,25 @@ export function StudioConsole() { } function appendAssistantError(message: string) { - setMessages((current) => [ - ...current, - { - entryId: nextTranscriptId(), - kind: "message", - role: "assistant", + setMessages((current) => { + const next = [...current]; + const last = next.at(-1); + const entry = { + entryId: + last?.kind === "message" && last.role === "assistant" && last.tone === "pending" + ? last.entryId + : nextTranscriptId(), + kind: "message" as const, + role: "assistant" as const, text: message, - tone: "error", - }, - ]); + tone: "error" as const, + }; + if (last?.kind === "message" && last.role === "assistant" && last.tone === "pending") { + next[next.length - 1] = entry; + return next; + } + return [...next, entry]; + }); } function assignAssistantTraceId(traceId: string) { @@ -1305,9 +1426,28 @@ export function StudioConsole() { }); } + function clearPendingAssistant() { + setMessages((current) => withoutPendingAssistant(current)); + } + + function withoutPendingAssistant(entries: TranscriptEntry[]): TranscriptEntry[] { + for (let index = entries.length - 1; index >= 0; index -= 1) { + const entry = entries[index]; + if ( + entry?.kind === "message" && + entry.role === "assistant" && + entry.tone === "pending" && + entry.text.trim().length === 0 + ) { + return entries.filter((_, itemIndex) => itemIndex !== index); + } + } + return [...entries]; + } + function updateToolApproval(approval: ToolApprovalUpdate) { setMessages((current) => { - const next = [...current]; + const next = withoutPendingAssistant(current); const matchedIndex = findMatchingToolIndexByCall(next, approval.toolName, approval.callId); if (matchedIndex < 0) { next.push({ @@ -1345,7 +1485,7 @@ export function StudioConsole() { function updateToolQuestion(question: ToolQuestionUpdate) { setMessages((current) => { - const next = [...current]; + const next = withoutPendingAssistant(current); const matchedIndex = findMatchingToolIndexByCall(next, question.toolName, question.callId); if (matchedIndex < 0) { next.push({ @@ -1446,7 +1586,7 @@ export function StudioConsole() { function appendReasoningText(delta: string, reasoningId: string | undefined) { setMessages((current) => { - const next = [...current]; + const next = withoutPendingAssistant(current); const last = next.at(-1); if (last?.kind === "reasoning" && (last.reasoningId ?? "") === (reasoningId ?? "")) { next[next.length - 1] = { ...last, text: `${last.text}${delta}` }; @@ -1464,7 +1604,7 @@ export function StudioConsole() { function appendToolCall(toolName: string, args: string, callId: string | undefined) { setMessages((current) => [ - ...current, + ...withoutPendingAssistant(current), { entryId: nextTranscriptId(), kind: "tool", @@ -1483,7 +1623,7 @@ export function StudioConsole() { structuredResult?: ToolResultContent[]; }) { setMessages((current) => { - const next = [...current]; + const next = withoutPendingAssistant(current); const matchedIndex = findMatchingToolIndex(next, props.toolName, props.callId); if (matchedIndex >= 0) { const existing = next[matchedIndex]; @@ -1521,7 +1661,7 @@ export function StudioConsole() { return; } setMessages((current) => { - const next = [...current]; + const next = withoutPendingAssistant(current); const matchedIndex = findMatchingToolIndex(next, event.toolName, event.toolCallId); if (matchedIndex < 0) { next.push({ @@ -1675,6 +1815,25 @@ export function StudioConsole() { resizeTextarea(event.currentTarget); } + async function addPromptAttachments(event: ChangeEvent) { + const files = Array.from(event.currentTarget.files ?? []); + event.currentTarget.value = ""; + if (files.length === 0) { + return; + } + + try { + const nextAttachments = await Promise.all(files.map(fileToAttachment)); + setAttachments((current) => [...current, ...nextAttachments]); + } catch (attachmentError) { + setError(errorMessage(attachmentError)); + } + } + + function removePromptAttachment(id: string) { + setAttachments((current) => current.filter((attachment) => attachment.id !== id)); + } + function handlePromptKeyDown(event: KeyboardEvent) { if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) { return; @@ -1738,6 +1897,7 @@ export function StudioConsole() { setSessionLogs([]); setMessages([]); setPrompt(""); + setAttachments([]); setActivePage(nextPage); updatePagePath(nextPage); @@ -2050,9 +2210,74 @@ export function StudioConsole() { onKeyDown={handlePromptKeyDown} placeholder="Ask anything..." /> + {attachments.length === 0 ? null : ( +
+ {attachments.map((attachment) => ( + + + {attachment.kind === "image" ? "Image" : "Doc"} / {attachment.name} + + + + ))} +
+ )}
-
+ void addPromptAttachments(event)} + /> + +
+
+ {selectedAgentModels.length === 0 ? null : ( + + )} {agents.length > 1 ? (