Merge branch 'main' into openai-vector-store/qdrant

This commit is contained in:
Francisco Arceo 2025-08-01 00:39:03 -04:00 committed by GitHub
commit 354f79f15c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 2411 additions and 2137 deletions

View file

@ -13,6 +13,7 @@ Llama Stack uses GitHub Actions for Continuous Integration (CI). Below is a tabl
| Pre-commit | [pre-commit.yml](pre-commit.yml) | Run pre-commit checks | | Pre-commit | [pre-commit.yml](pre-commit.yml) | Run pre-commit checks |
| Test Llama Stack Build | [providers-build.yml](providers-build.yml) | Test llama stack build | | Test Llama Stack Build | [providers-build.yml](providers-build.yml) | Test llama stack build |
| Python Package Build Test | [python-build-test.yml](python-build-test.yml) | Test building the llama-stack PyPI project | | Python Package Build Test | [python-build-test.yml](python-build-test.yml) | Test building the llama-stack PyPI project |
| Integration Tests (Record) | [record-integration-tests.yml](record-integration-tests.yml) | Run the integration test suite from tests/integration |
| Check semantic PR titles | [semantic-pr.yml](semantic-pr.yml) | Ensure that PR titles follow the conventional commit spec | | Check semantic PR titles | [semantic-pr.yml](semantic-pr.yml) | Ensure that PR titles follow the conventional commit spec |
| Close stale issues and PRs | [stale_bot.yml](stale_bot.yml) | Run the Stale Bot action | | Close stale issues and PRs | [stale_bot.yml](stale_bot.yml) | Run the Stale Bot action |
| Test External Providers Installed via Module | [test-external-provider-module.yml](test-external-provider-module.yml) | Test External Provider installation via Python module | | Test External Providers Installed via Module | [test-external-provider-module.yml](test-external-provider-module.yml) | Test External Provider installation via Python module |

View file

@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
vector-io-provider: ["inline::faiss", "inline::sqlite-vec", "inline::milvus", "remote::chromadb", "remote::pgvector", "remote::qdrant"] vector-io-provider: ["inline::faiss", "inline::sqlite-vec", "inline::milvus", "remote::chromadb", "remote::pgvector", "remote::weaviate", "remote::qdrant"]
python-version: ["3.12", "3.13"] python-version: ["3.12", "3.13"]
fail-fast: false # we want to run all tests regardless of failure fail-fast: false # we want to run all tests regardless of failure
@ -48,6 +48,14 @@ jobs:
-e ANONYMIZED_TELEMETRY=FALSE \ -e ANONYMIZED_TELEMETRY=FALSE \
chromadb/chroma:latest chromadb/chroma:latest
- name: Setup Weaviate
if: matrix.vector-io-provider == 'remote::weaviate'
run: |
docker run --rm -d --pull always \
--name weaviate \
-p 8080:8080 -p 50051:50051 \
cr.weaviate.io/semitechnologies/weaviate:1.32.0
- name: Start PGVector DB - name: Start PGVector DB
if: matrix.vector-io-provider == 'remote::pgvector' if: matrix.vector-io-provider == 'remote::pgvector'
run: | run: |
@ -116,6 +124,21 @@ jobs:
docker logs chromadb docker logs chromadb
exit 1 exit 1
- name: Wait for Weaviate to be ready
if: matrix.vector-io-provider == 'remote::weaviate'
run: |
echo "Waiting for Weaviate to be ready..."
for i in {1..30}; do
if curl -s http://localhost:8080 | grep -q "https://weaviate.io/developers/weaviate/current/"; then
echo "Weaviate is ready!"
exit 0
fi
sleep 2
done
echo "Weaviate failed to start"
docker logs weaviate
exit 1
- name: Build Llama Stack - name: Build Llama Stack
run: | run: |
uv run llama stack build --template ci-tests --image-type venv uv run llama stack build --template ci-tests --image-type venv
@ -138,6 +161,8 @@ jobs:
PGVECTOR_PASSWORD: ${{ matrix.vector-io-provider == 'remote::pgvector' && 'llamastack' || '' }} PGVECTOR_PASSWORD: ${{ matrix.vector-io-provider == 'remote::pgvector' && 'llamastack' || '' }}
ENABLE_QDRANT: ${{ matrix.vector-io-provider == 'remote::qdrant' && 'true' || '' }} ENABLE_QDRANT: ${{ matrix.vector-io-provider == 'remote::qdrant' && 'true' || '' }}
QDRANT_URL: ${{ matrix.vector-io-provider == 'remote::qdrant' && 'http://localhost:6333' || '' }} QDRANT_URL: ${{ matrix.vector-io-provider == 'remote::qdrant' && 'http://localhost:6333' || '' }}
ENABLE_WEAVIATE: ${{ matrix.vector-io-provider == 'remote::weaviate' && 'true' || '' }}
WEAVIATE_CLUSTER_URL: ${{ matrix.vector-io-provider == 'remote::weaviate' && 'localhost:8080' || '' }}
run: | run: |
uv run pytest -sv --stack-config="inference=inline::sentence-transformers,vector_io=${{ matrix.vector-io-provider }}" \ uv run pytest -sv --stack-config="inference=inline::sentence-transformers,vector_io=${{ matrix.vector-io-provider }}" \
tests/integration/vector_io \ tests/integration/vector_io \

View file

@ -0,0 +1,109 @@
name: Integration Tests (Record)
run-name: Run the integration test suite from tests/integration
on:
pull_request:
branches: [ main ]
types: [opened, synchronize, labeled]
paths:
- 'llama_stack/**'
- 'tests/**'
- 'uv.lock'
- 'pyproject.toml'
- '.github/workflows/record-integration-tests.yml' # This workflow
- '.github/actions/setup-ollama/action.yml'
- '.github/actions/setup-test-environment/action.yml'
- '.github/actions/run-and-record-tests/action.yml'
workflow_dispatch:
inputs:
test-provider:
description: 'Test against a specific provider'
type: string
default: 'ollama'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
discover-tests:
if: contains(github.event.pull_request.labels.*.name, 're-record-tests') ||
contains(github.event.pull_request.labels.*.name, 're-record-vision-tests')
runs-on: ubuntu-latest
outputs:
test-types: ${{ steps.generate-test-types.outputs.test-types }}
matrix-modes: ${{ steps.generate-test-types.outputs.matrix-modes }}
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Generate test types
id: generate-test-types
run: |
# Get test directories dynamically, excluding non-test directories
TEST_TYPES=$(find tests/integration -maxdepth 1 -mindepth 1 -type d -printf "%f\n" |
grep -Ev "^(__pycache__|fixtures|test_cases|recordings|post_training)$" |
sort | jq -R -s -c 'split("\n")[:-1]')
echo "test-types=$TEST_TYPES" >> $GITHUB_OUTPUT
labels=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name')
echo "labels=$labels"
modes_array=()
if [[ $labels == *"re-record-vision-tests"* ]]; then
modes_array+=("vision")
fi
if [[ $labels == *"re-record-tests"* ]]; then
modes_array+=("non-vision")
fi
# Convert to JSON array
if [ ${#modes_array[@]} -eq 0 ]; then
matrix_modes="[]"
else
matrix_modes=$(printf '%s\n' "${modes_array[@]}" | jq -R -s -c 'split("\n")[:-1]')
fi
echo "matrix_modes=$matrix_modes"
echo "matrix-modes=$matrix_modes" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ github.token }}
record-tests:
needs: discover-tests
runs-on: ubuntu-latest
permissions:
contents: write
strategy:
fail-fast: false
matrix:
mode: ${{ fromJSON(needs.discover-tests.outputs.matrix-modes) }}
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Setup test environment
uses: ./.github/actions/setup-test-environment
with:
python-version: "3.12" # Use single Python version for recording
client-version: "latest"
provider: ${{ inputs.test-provider || 'ollama' }}
run-vision-tests: ${{ matrix.mode == 'vision' && 'true' || 'false' }}
inference-mode: 'record'
- name: Run and record tests
uses: ./.github/actions/run-and-record-tests
with:
test-types: ${{ needs.discover-tests.outputs.test-types }}
stack-config: 'server:ci-tests' # recording must be done with server since more tests are run
provider: ${{ inputs.test-provider || 'ollama' }}
inference-mode: 'record'
run-vision-tests: ${{ matrix.mode == 'vision' && 'true' || 'false' }}

View file

@ -33,9 +33,19 @@ To install Weaviate see the [Weaviate quickstart documentation](https://weaviate
See [Weaviate's documentation](https://weaviate.io/developers/weaviate) for more details about Weaviate in general. See [Weaviate's documentation](https://weaviate.io/developers/weaviate) for more details about Weaviate in general.
## Configuration
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `weaviate_api_key` | `str \| None` | No | | The API key for the Weaviate instance |
| `weaviate_cluster_url` | `str \| None` | No | localhost:8080 | The URL of the Weaviate cluster |
| `kvstore` | `utils.kvstore.config.RedisKVStoreConfig \| utils.kvstore.config.SqliteKVStoreConfig \| utils.kvstore.config.PostgresKVStoreConfig \| utils.kvstore.config.MongoDBKVStoreConfig, annotation=NoneType, required=False, default='sqlite', discriminator='type'` | No | | Config for KV store backend (SQLite only for now) |
## Sample Configuration ## Sample Configuration
```yaml ```yaml
weaviate_api_key: null
weaviate_cluster_url: ${env.WEAVIATE_CLUSTER_URL:=localhost:8080}
kvstore: kvstore:
type: sqlite type: sqlite
db_path: ${env.SQLITE_STORE_DIR:=~/.llama/dummy}/weaviate_registry.db db_path: ${env.SQLITE_STORE_DIR:=~/.llama/dummy}/weaviate_registry.db

View file

@ -16,7 +16,7 @@ from pydantic import BaseModel, Field
from llama_stack.apis.inference import InterleavedContent from llama_stack.apis.inference import InterleavedContent
from llama_stack.apis.vector_dbs import VectorDB from llama_stack.apis.vector_dbs import VectorDB
from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol
from llama_stack.providers.utils.vector_io.chunk_utils import generate_chunk_id from llama_stack.providers.utils.vector_io.vector_utils import generate_chunk_id
from llama_stack.schema_utils import json_schema_type, webmethod from llama_stack.schema_utils import json_schema_type, webmethod
from llama_stack.strong_typing.schema import register_schema from llama_stack.strong_typing.schema import register_schema

View file

@ -7,7 +7,6 @@
import asyncio import asyncio
import logging import logging
import os import os
import re
from typing import Any from typing import Any
from numpy.typing import NDArray from numpy.typing import NDArray
@ -31,6 +30,7 @@ from llama_stack.providers.utils.memory.vector_store import (
EmbeddingIndex, EmbeddingIndex,
VectorDBWithIndex, VectorDBWithIndex,
) )
from llama_stack.providers.utils.vector_io.vector_utils import sanitize_collection_name
from .config import MilvusVectorIOConfig as RemoteMilvusVectorIOConfig from .config import MilvusVectorIOConfig as RemoteMilvusVectorIOConfig
@ -44,14 +44,6 @@ OPENAI_VECTOR_STORES_FILES_PREFIX = f"openai_vector_stores_files:milvus:{VERSION
OPENAI_VECTOR_STORES_FILES_CONTENTS_PREFIX = f"openai_vector_stores_files_contents:milvus:{VERSION}::" OPENAI_VECTOR_STORES_FILES_CONTENTS_PREFIX = f"openai_vector_stores_files_contents:milvus:{VERSION}::"
def sanitize_collection_name(name: str) -> str:
"""
Sanitize collection name to ensure it only contains numbers, letters, and underscores.
Any other characters are replaced with underscores.
"""
return re.sub(r"[^a-zA-Z0-9_]", "_", name)
class MilvusIndex(EmbeddingIndex): class MilvusIndex(EmbeddingIndex):
def __init__( def __init__(
self, client: MilvusClient, collection_name: str, consistency_level="Strong", kvstore: KVStore | None = None self, client: MilvusClient, collection_name: str, consistency_level="Strong", kvstore: KVStore | None = None

View file

@ -12,6 +12,6 @@ from .config import WeaviateVectorIOConfig
async def get_adapter_impl(config: WeaviateVectorIOConfig, deps: dict[Api, ProviderSpec]): async def get_adapter_impl(config: WeaviateVectorIOConfig, deps: dict[Api, ProviderSpec]):
from .weaviate import WeaviateVectorIOAdapter from .weaviate import WeaviateVectorIOAdapter
impl = WeaviateVectorIOAdapter(config, deps[Api.inference]) impl = WeaviateVectorIOAdapter(config, deps[Api.inference], deps.get(Api.files, None))
await impl.initialize() await impl.initialize()
return impl return impl

View file

@ -12,18 +12,24 @@ from llama_stack.providers.utils.kvstore.config import (
KVStoreConfig, KVStoreConfig,
SqliteKVStoreConfig, SqliteKVStoreConfig,
) )
from llama_stack.schema_utils import json_schema_type
class WeaviateRequestProviderData(BaseModel): @json_schema_type
weaviate_api_key: str class WeaviateVectorIOConfig(BaseModel):
weaviate_cluster_url: str weaviate_api_key: str | None = Field(description="The API key for the Weaviate instance", default=None)
weaviate_cluster_url: str | None = Field(description="The URL of the Weaviate cluster", default="localhost:8080")
kvstore: KVStoreConfig | None = Field(description="Config for KV store backend (SQLite only for now)", default=None) kvstore: KVStoreConfig | None = Field(description="Config for KV store backend (SQLite only for now)", default=None)
class WeaviateVectorIOConfig(BaseModel):
@classmethod @classmethod
def sample_run_config(cls, __distro_dir__: str, **kwargs: Any) -> dict[str, Any]: def sample_run_config(
cls,
__distro_dir__: str,
**kwargs: Any,
) -> dict[str, Any]:
return { return {
"weaviate_api_key": None,
"weaviate_cluster_url": "${env.WEAVIATE_CLUSTER_URL:=localhost:8080}",
"kvstore": SqliteKVStoreConfig.sample_run_config( "kvstore": SqliteKVStoreConfig.sample_run_config(
__distro_dir__=__distro_dir__, __distro_dir__=__distro_dir__,
db_name="weaviate_registry.db", db_name="weaviate_registry.db",

View file

@ -22,12 +22,16 @@ from llama_stack.core.request_headers import NeedsRequestProviderData
from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate
from llama_stack.providers.utils.kvstore import kvstore_impl from llama_stack.providers.utils.kvstore import kvstore_impl
from llama_stack.providers.utils.kvstore.api import KVStore from llama_stack.providers.utils.kvstore.api import KVStore
from llama_stack.providers.utils.memory.openai_vector_store_mixin import (
OpenAIVectorStoreMixin,
)
from llama_stack.providers.utils.memory.vector_store import ( from llama_stack.providers.utils.memory.vector_store import (
EmbeddingIndex, EmbeddingIndex,
VectorDBWithIndex, VectorDBWithIndex,
) )
from llama_stack.providers.utils.vector_io.vector_utils import sanitize_collection_name
from .config import WeaviateRequestProviderData, WeaviateVectorIOConfig from .config import WeaviateVectorIOConfig
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -40,11 +44,19 @@ OPENAI_VECTOR_STORES_FILES_CONTENTS_PREFIX = f"openai_vector_stores_files_conten
class WeaviateIndex(EmbeddingIndex): class WeaviateIndex(EmbeddingIndex):
def __init__(self, client: weaviate.Client, collection_name: str, kvstore: KVStore | None = None): def __init__(
self,
client: weaviate.Client,
collection_name: str,
kvstore: KVStore | None = None,
):
self.client = client self.client = client
self.collection_name = collection_name self.collection_name = sanitize_collection_name(collection_name, weaviate_format=True)
self.kvstore = kvstore self.kvstore = kvstore
async def initialize(self):
pass
async def add_chunks(self, chunks: list[Chunk], embeddings: NDArray): async def add_chunks(self, chunks: list[Chunk], embeddings: NDArray):
assert len(chunks) == len(embeddings), ( assert len(chunks) == len(embeddings), (
f"Chunk length {len(chunks)} does not match embedding length {len(embeddings)}" f"Chunk length {len(chunks)} does not match embedding length {len(embeddings)}"
@ -68,10 +80,13 @@ class WeaviateIndex(EmbeddingIndex):
collection.data.insert_many(data_objects) collection.data.insert_many(data_objects)
async def delete_chunk(self, chunk_id: str) -> None: async def delete_chunk(self, chunk_id: str) -> None:
raise NotImplementedError("delete_chunk is not supported in Chroma") sanitized_collection_name = sanitize_collection_name(self.collection_name, weaviate_format=True)
collection = self.client.collections.get(sanitized_collection_name)
collection.data.delete_many(where=Filter.by_property("id").contains_any([chunk_id]))
async def query_vector(self, embedding: NDArray, k: int, score_threshold: float) -> QueryChunksResponse: async def query_vector(self, embedding: NDArray, k: int, score_threshold: float) -> QueryChunksResponse:
collection = self.client.collections.get(self.collection_name) sanitized_collection_name = sanitize_collection_name(self.collection_name, weaviate_format=True)
collection = self.client.collections.get(sanitized_collection_name)
results = collection.query.near_vector( results = collection.query.near_vector(
near_vector=embedding.tolist(), near_vector=embedding.tolist(),
@ -95,8 +110,17 @@ class WeaviateIndex(EmbeddingIndex):
return QueryChunksResponse(chunks=chunks, scores=scores) return QueryChunksResponse(chunks=chunks, scores=scores)
async def delete(self, chunk_ids: list[str]) -> None: async def delete(self, chunk_ids: list[str] | None = None) -> None:
collection = self.client.collections.get(self.collection_name) """
Delete chunks by IDs if provided, otherwise drop the entire collection.
"""
sanitized_collection_name = sanitize_collection_name(self.collection_name, weaviate_format=True)
if chunk_ids is None:
# Drop entire collection if it exists
if self.client.collections.exists(sanitized_collection_name):
self.client.collections.delete(sanitized_collection_name)
return
collection = self.client.collections.get(sanitized_collection_name)
collection.data.delete_many(where=Filter.by_property("id").contains_any(chunk_ids)) collection.data.delete_many(where=Filter.by_property("id").contains_any(chunk_ids))
async def query_keyword( async def query_keyword(
@ -120,6 +144,7 @@ class WeaviateIndex(EmbeddingIndex):
class WeaviateVectorIOAdapter( class WeaviateVectorIOAdapter(
OpenAIVectorStoreMixin,
VectorIO, VectorIO,
NeedsRequestProviderData, NeedsRequestProviderData,
VectorDBsProtocolPrivate, VectorDBsProtocolPrivate,
@ -141,42 +166,56 @@ class WeaviateVectorIOAdapter(
self.metadata_collection_name = "openai_vector_stores_metadata" self.metadata_collection_name = "openai_vector_stores_metadata"
def _get_client(self) -> weaviate.Client: def _get_client(self) -> weaviate.Client:
provider_data = self.get_request_provider_data() if "localhost" in self.config.weaviate_cluster_url:
assert provider_data is not None, "Request provider data must be set" log.info("using Weaviate locally in container")
assert isinstance(provider_data, WeaviateRequestProviderData) host, port = self.config.weaviate_cluster_url.split(":")
key = "local_test"
key = f"{provider_data.weaviate_cluster_url}::{provider_data.weaviate_api_key}" client = weaviate.connect_to_local(
if key in self.client_cache: host=host,
return self.client_cache[key] port=port,
)
client = weaviate.connect_to_weaviate_cloud( else:
cluster_url=provider_data.weaviate_cluster_url, log.info("Using Weaviate remote cluster with URL")
auth_credentials=Auth.api_key(provider_data.weaviate_api_key), key = f"{self.config.weaviate_cluster_url}::{self.config.weaviate_api_key}"
) if key in self.client_cache:
return self.client_cache[key]
client = weaviate.connect_to_weaviate_cloud(
cluster_url=self.config.weaviate_cluster_url,
auth_credentials=Auth.api_key(self.config.weaviate_api_key),
)
self.client_cache[key] = client self.client_cache[key] = client
return client return client
async def initialize(self) -> None: async def initialize(self) -> None:
"""Set up KV store and load existing vector DBs and OpenAI vector stores.""" """Set up KV store and load existing vector DBs and OpenAI vector stores."""
# Initialize KV store for metadata # Initialize KV store for metadata if configured
self.kvstore = await kvstore_impl(self.config.kvstore) if self.config.kvstore is not None:
self.kvstore = await kvstore_impl(self.config.kvstore)
else:
self.kvstore = None
log.info("No kvstore configured, registry will not persist across restarts")
# Load existing vector DB definitions # Load existing vector DB definitions
start_key = VECTOR_DBS_PREFIX if self.kvstore is not None:
end_key = f"{VECTOR_DBS_PREFIX}\xff" start_key = VECTOR_DBS_PREFIX
stored = await self.kvstore.values_in_range(start_key, end_key) end_key = f"{VECTOR_DBS_PREFIX}\xff"
for raw in stored: stored = await self.kvstore.values_in_range(start_key, end_key)
vector_db = VectorDB.model_validate_json(raw) for raw in stored:
client = self._get_client() vector_db = VectorDB.model_validate_json(raw)
idx = WeaviateIndex(client=client, collection_name=vector_db.identifier, kvstore=self.kvstore) client = self._get_client()
self.cache[vector_db.identifier] = VectorDBWithIndex( idx = WeaviateIndex(
vector_db=vector_db, client=client,
index=idx, collection_name=vector_db.identifier,
inference_api=self.inference_api, kvstore=self.kvstore,
) )
self.cache[vector_db.identifier] = VectorDBWithIndex(
vector_db=vector_db,
index=idx,
inference_api=self.inference_api,
)
# Load OpenAI vector stores metadata into cache # Load OpenAI vector stores metadata into cache
await self.initialize_openai_vector_stores() await self.initialize_openai_vector_stores()
async def shutdown(self) -> None: async def shutdown(self) -> None:
for client in self.client_cache.values(): for client in self.client_cache.values():
@ -187,11 +226,11 @@ class WeaviateVectorIOAdapter(
vector_db: VectorDB, vector_db: VectorDB,
) -> None: ) -> None:
client = self._get_client() client = self._get_client()
sanitized_collection_name = sanitize_collection_name(vector_db.identifier, weaviate_format=True)
# Create collection if it doesn't exist # Create collection if it doesn't exist
if not client.collections.exists(vector_db.identifier): if not client.collections.exists(sanitized_collection_name):
client.collections.create( client.collections.create(
name=vector_db.identifier, name=sanitized_collection_name,
vectorizer_config=wvc.config.Configure.Vectorizer.none(), vectorizer_config=wvc.config.Configure.Vectorizer.none(),
properties=[ properties=[
wvc.config.Property( wvc.config.Property(
@ -201,30 +240,41 @@ class WeaviateVectorIOAdapter(
], ],
) )
self.cache[vector_db.identifier] = VectorDBWithIndex( self.cache[sanitized_collection_name] = VectorDBWithIndex(
vector_db, vector_db,
WeaviateIndex(client=client, collection_name=vector_db.identifier), WeaviateIndex(client=client, collection_name=sanitized_collection_name),
self.inference_api, self.inference_api,
) )
async def _get_and_cache_vector_db_index(self, vector_db_id: str) -> VectorDBWithIndex | None: async def unregister_vector_db(self, vector_db_id: str) -> None:
if vector_db_id in self.cache: client = self._get_client()
return self.cache[vector_db_id] sanitized_collection_name = sanitize_collection_name(vector_db_id, weaviate_format=True)
if sanitized_collection_name not in self.cache or client.collections.exists(sanitized_collection_name) is False:
log.warning(f"Vector DB {sanitized_collection_name} not found")
return
client.collections.delete(sanitized_collection_name)
await self.cache[sanitized_collection_name].index.delete()
del self.cache[sanitized_collection_name]
vector_db = await self.vector_db_store.get_vector_db(vector_db_id) async def _get_and_cache_vector_db_index(self, vector_db_id: str) -> VectorDBWithIndex | None:
sanitized_collection_name = sanitize_collection_name(vector_db_id, weaviate_format=True)
if sanitized_collection_name in self.cache:
return self.cache[sanitized_collection_name]
vector_db = await self.vector_db_store.get_vector_db(sanitized_collection_name)
if not vector_db: if not vector_db:
raise VectorStoreNotFoundError(vector_db_id) raise VectorStoreNotFoundError(vector_db_id)
client = self._get_client() client = self._get_client()
if not client.collections.exists(vector_db.identifier): if not client.collections.exists(vector_db.identifier):
raise ValueError(f"Collection with name `{vector_db.identifier}` not found") raise ValueError(f"Collection with name `{sanitized_collection_name}` not found")
index = VectorDBWithIndex( index = VectorDBWithIndex(
vector_db=vector_db, vector_db=vector_db,
index=WeaviateIndex(client=client, collection_name=vector_db.identifier), index=WeaviateIndex(client=client, collection_name=sanitized_collection_name),
inference_api=self.inference_api, inference_api=self.inference_api,
) )
self.cache[vector_db_id] = index self.cache[sanitized_collection_name] = index
return index return index
async def insert_chunks( async def insert_chunks(
@ -233,7 +283,8 @@ class WeaviateVectorIOAdapter(
chunks: list[Chunk], chunks: list[Chunk],
ttl_seconds: int | None = None, ttl_seconds: int | None = None,
) -> None: ) -> None:
index = await self._get_and_cache_vector_db_index(vector_db_id) sanitized_collection_name = sanitize_collection_name(vector_db_id, weaviate_format=True)
index = await self._get_and_cache_vector_db_index(sanitized_collection_name)
if not index: if not index:
raise VectorStoreNotFoundError(vector_db_id) raise VectorStoreNotFoundError(vector_db_id)
@ -245,29 +296,17 @@ class WeaviateVectorIOAdapter(
query: InterleavedContent, query: InterleavedContent,
params: dict[str, Any] | None = None, params: dict[str, Any] | None = None,
) -> QueryChunksResponse: ) -> QueryChunksResponse:
index = await self._get_and_cache_vector_db_index(vector_db_id) sanitized_collection_name = sanitize_collection_name(vector_db_id, weaviate_format=True)
index = await self._get_and_cache_vector_db_index(sanitized_collection_name)
if not index: if not index:
raise VectorStoreNotFoundError(vector_db_id) raise VectorStoreNotFoundError(vector_db_id)
return await index.query_chunks(query, params) return await index.query_chunks(query, params)
# OpenAI Vector Stores File operations are not supported in Weaviate
async def _save_openai_vector_store_file(
self, store_id: str, file_id: str, file_info: dict[str, Any], file_contents: list[dict[str, Any]]
) -> None:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate")
async def _load_openai_vector_store_file(self, store_id: str, file_id: str) -> dict[str, Any]:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate")
async def _load_openai_vector_store_file_contents(self, store_id: str, file_id: str) -> list[dict[str, Any]]:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate")
async def _update_openai_vector_store_file(self, store_id: str, file_id: str, file_info: dict[str, Any]) -> None:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate")
async def _delete_openai_vector_store_file_from_storage(self, store_id: str, file_id: str) -> None:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate")
async def delete_chunks(self, store_id: str, chunk_ids: list[str]) -> None: async def delete_chunks(self, store_id: str, chunk_ids: list[str]) -> None:
raise NotImplementedError("OpenAI Vector Stores API is not supported in Weaviate") sanitized_collection_name = sanitize_collection_name(store_id, weaviate_format=True)
index = await self._get_and_cache_vector_db_index(sanitized_collection_name)
if not index:
raise ValueError(f"Vector DB {sanitized_collection_name} not found")
await index.delete(chunk_ids)

View file

@ -30,7 +30,7 @@ from llama_stack.providers.datatypes import Api
from llama_stack.providers.utils.inference.prompt_adapter import ( from llama_stack.providers.utils.inference.prompt_adapter import (
interleaved_content_as_str, interleaved_content_as_str,
) )
from llama_stack.providers.utils.vector_io.chunk_utils import generate_chunk_id from llama_stack.providers.utils.vector_io.vector_utils import generate_chunk_id
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View file

@ -5,6 +5,7 @@
# the root directory of this source tree. # the root directory of this source tree.
import hashlib import hashlib
import re
import uuid import uuid
@ -19,3 +20,20 @@ def generate_chunk_id(document_id: str, chunk_text: str, chunk_window: str | Non
if chunk_window: if chunk_window:
hash_input += f":{chunk_window}".encode() hash_input += f":{chunk_window}".encode()
return str(uuid.UUID(hashlib.md5(hash_input, usedforsecurity=False).hexdigest())) return str(uuid.UUID(hashlib.md5(hash_input, usedforsecurity=False).hexdigest()))
def proper_case(s: str) -> str:
"""Convert a string to proper case (first letter uppercase, rest lowercase)."""
return s[0].upper() + s[1:].lower() if s else s
def sanitize_collection_name(name: str, weaviate_format=False) -> str:
"""
Sanitize collection name to ensure it only contains numbers, letters, and underscores.
Any other characters are replaced with underscores.
"""
if not weaviate_format:
s = re.sub(r"[^a-zA-Z0-9_]", "_", name)
else:
s = proper_case(re.sub(r"[^a-zA-Z0-9]", "", name))
return s

View file

@ -116,6 +116,7 @@ test = [
"requests", "requests",
"pymilvus>=2.5.12", "pymilvus>=2.5.12",
"reportlab", "reportlab",
"weaviate-client>=4.16.4",
] ]
docs = [ docs = [
"setuptools", "setuptools",

View file

@ -6,6 +6,7 @@
import inspect import inspect
import os import os
import shlex
import signal import signal
import socket import socket
import subprocess import subprocess
@ -38,10 +39,10 @@ def is_port_available(port: int, host: str = "localhost") -> bool:
def start_llama_stack_server(config_name: str) -> subprocess.Popen: def start_llama_stack_server(config_name: str) -> subprocess.Popen:
"""Start a llama stack server with the given config.""" """Start a llama stack server with the given config."""
cmd = ["llama", "stack", "run", config_name] cmd = f"uv run --with llama-stack llama stack build --template {config_name} --image-type venv --run"
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
process = subprocess.Popen( process = subprocess.Popen(
cmd, shlex.split(cmd),
stdout=devnull, # redirect stdout to devnull to prevent deadlock stdout=devnull, # redirect stdout to devnull to prevent deadlock
stderr=subprocess.PIPE, # keep stderr to see errors stderr=subprocess.PIPE, # keep stderr to see errors
text=True, text=True,

View file

@ -14,7 +14,7 @@
"models": [ "models": [
{ {
"model": "nomic-embed-text:latest", "model": "nomic-embed-text:latest",
"modified_at": "2025-07-31T17:55:10.302292Z", "modified_at": "2025-07-31T23:55:40.635067Z",
"digest": "0a109f422b47e3a30ba2b10eca18548e944e8a23073ee3f3e947efcf3c45e59f", "digest": "0a109f422b47e3a30ba2b10eca18548e944e8a23073ee3f3e947efcf3c45e59f",
"size": 274302450, "size": 274302450,
"details": { "details": {

View file

@ -30,15 +30,15 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:05:27.306789902Z", "created_at": "2025-08-01T00:06:12.068973125Z",
"done": true, "done": true,
"done_reason": "stop", "done_reason": "stop",
"total_duration": 40783180279, "total_duration": 44793549354,
"load_duration": 50835227, "load_duration": 51960915,
"prompt_eval_count": 18, "prompt_eval_count": 18,
"prompt_eval_duration": 518613186, "prompt_eval_duration": 579363429,
"eval_count": 110, "eval_count": 110,
"eval_duration": 40206118132, "eval_duration": 44156162976,
"message": { "message": {
"role": "assistant", "role": "assistant",
"content": "The image features a close-up of a golden retriever puppy, with its mouth open and tongue out, as if it is smiling or panting. The puppy's fur is a light golden color, and its ears are floppy and hanging down on either side of its head. The background of the image is blurred, but it appears to be a natural setting, possibly a field or a park, with a greenish-yellow color. The overall atmosphere of the image is one of happiness and playfulness, as the puppy seems to be enjoying itself.", "content": "The image features a close-up of a golden retriever puppy, with its mouth open and tongue out, as if it is smiling or panting. The puppy's fur is a light golden color, and its ears are floppy and hanging down on either side of its head. The background of the image is blurred, but it appears to be a natural setting, possibly a field or a park, with a greenish-yellow color. The overall atmosphere of the image is one of happiness and playfulness, as the puppy seems to be enjoying itself.",

View file

@ -31,7 +31,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:11.793039554Z", "created_at": "2025-08-01T00:04:49.339347876Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -53,7 +53,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:12.165542521Z", "created_at": "2025-08-01T00:04:49.747466769Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -75,7 +75,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:12.543284243Z", "created_at": "2025-08-01T00:04:50.156146804Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -97,7 +97,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:12.916189133Z", "created_at": "2025-08-01T00:04:50.566195243Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -119,7 +119,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:13.28189993Z", "created_at": "2025-08-01T00:04:50.975121211Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -141,7 +141,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:13.653725224Z", "created_at": "2025-08-01T00:04:51.388779549Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -163,7 +163,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:14.018988652Z", "created_at": "2025-08-01T00:04:51.79897453Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -185,7 +185,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:14.382645858Z", "created_at": "2025-08-01T00:04:52.209608504Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -207,7 +207,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:14.739580312Z", "created_at": "2025-08-01T00:04:52.619045995Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -229,7 +229,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:15.118228755Z", "created_at": "2025-08-01T00:04:53.026501007Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -251,7 +251,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:15.499394357Z", "created_at": "2025-08-01T00:04:53.436015071Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -273,7 +273,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:15.862659022Z", "created_at": "2025-08-01T00:04:53.843369446Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -295,7 +295,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:16.234769542Z", "created_at": "2025-08-01T00:04:54.255794451Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -317,7 +317,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:16.607886498Z", "created_at": "2025-08-01T00:04:54.663263793Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -339,7 +339,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:16.971019788Z", "created_at": "2025-08-01T00:04:55.073162133Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -361,7 +361,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:17.341647677Z", "created_at": "2025-08-01T00:04:55.48667439Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -383,7 +383,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:17.7063123Z", "created_at": "2025-08-01T00:04:55.897947147Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -405,7 +405,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:18.072790228Z", "created_at": "2025-08-01T00:04:56.31639321Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -427,7 +427,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:18.439855563Z", "created_at": "2025-08-01T00:04:56.729288843Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -449,7 +449,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:18.802891341Z", "created_at": "2025-08-01T00:04:57.142647132Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -471,7 +471,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:19.167811769Z", "created_at": "2025-08-01T00:04:57.55091814Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -493,7 +493,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:19.538569316Z", "created_at": "2025-08-01T00:04:57.959494633Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -515,7 +515,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:19.900014289Z", "created_at": "2025-08-01T00:04:58.367117419Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -537,7 +537,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:20.296603174Z", "created_at": "2025-08-01T00:04:58.77560425Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -559,7 +559,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:20.670619107Z", "created_at": "2025-08-01T00:04:59.183890868Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -581,7 +581,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:21.037515143Z", "created_at": "2025-08-01T00:04:59.596163097Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -603,7 +603,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:21.41078269Z", "created_at": "2025-08-01T00:05:00.004002773Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -625,7 +625,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:21.773638843Z", "created_at": "2025-08-01T00:05:00.410717383Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -647,7 +647,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:22.134453183Z", "created_at": "2025-08-01T00:05:00.817783323Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -669,7 +669,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:22.494472509Z", "created_at": "2025-08-01T00:05:01.223523865Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -691,7 +691,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:22.863772543Z", "created_at": "2025-08-01T00:05:01.63351174Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -713,7 +713,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:23.231941325Z", "created_at": "2025-08-01T00:05:02.032702205Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -735,7 +735,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:23.592854692Z", "created_at": "2025-08-01T00:05:02.424431407Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -757,7 +757,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:23.959965309Z", "created_at": "2025-08-01T00:05:02.81524835Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -779,7 +779,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:24.322278389Z", "created_at": "2025-08-01T00:05:03.207597567Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -801,7 +801,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:24.690079541Z", "created_at": "2025-08-01T00:05:03.614094549Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -823,7 +823,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:25.080109961Z", "created_at": "2025-08-01T00:05:04.008232462Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -845,7 +845,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:25.501052051Z", "created_at": "2025-08-01T00:05:04.411085956Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -867,7 +867,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:25.874257383Z", "created_at": "2025-08-01T00:05:04.80616608Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -889,7 +889,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:26.240808197Z", "created_at": "2025-08-01T00:05:05.212911563Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -911,7 +911,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:26.633293446Z", "created_at": "2025-08-01T00:05:05.599645826Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -933,7 +933,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:26.996109719Z", "created_at": "2025-08-01T00:05:05.998590959Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -955,7 +955,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:27.358774996Z", "created_at": "2025-08-01T00:05:06.398745325Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -977,7 +977,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:27.731898916Z", "created_at": "2025-08-01T00:05:06.790505624Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -999,7 +999,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:28.096809626Z", "created_at": "2025-08-01T00:05:07.199713609Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1021,7 +1021,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:28.479909418Z", "created_at": "2025-08-01T00:05:07.596500603Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1043,7 +1043,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:28.842369829Z", "created_at": "2025-08-01T00:05:07.997793386Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1065,7 +1065,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:29.219065256Z", "created_at": "2025-08-01T00:05:08.381509773Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1087,7 +1087,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:29.586481232Z", "created_at": "2025-08-01T00:05:08.76579698Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1109,7 +1109,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:29.951118759Z", "created_at": "2025-08-01T00:05:09.159673897Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1131,7 +1131,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:30.341772269Z", "created_at": "2025-08-01T00:05:09.557596611Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1153,7 +1153,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:30.715938769Z", "created_at": "2025-08-01T00:05:09.950543555Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1175,7 +1175,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:31.089904077Z", "created_at": "2025-08-01T00:05:10.351722165Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1197,7 +1197,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:31.464995093Z", "created_at": "2025-08-01T00:05:10.752622361Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1219,7 +1219,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:31.830372387Z", "created_at": "2025-08-01T00:05:11.15541961Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1241,7 +1241,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:32.196652344Z", "created_at": "2025-08-01T00:05:11.549741697Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1263,7 +1263,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:32.564551739Z", "created_at": "2025-08-01T00:05:11.935619908Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1285,7 +1285,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:32.929462678Z", "created_at": "2025-08-01T00:05:12.343367145Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1307,7 +1307,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:33.302119433Z", "created_at": "2025-08-01T00:05:12.745897023Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1329,7 +1329,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:33.664518348Z", "created_at": "2025-08-01T00:05:13.148396264Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1351,7 +1351,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:34.025969567Z", "created_at": "2025-08-01T00:05:13.549096782Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1373,7 +1373,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:34.387819486Z", "created_at": "2025-08-01T00:05:13.945126876Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1395,7 +1395,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:34.755034711Z", "created_at": "2025-08-01T00:05:14.351732762Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1417,7 +1417,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:35.152309014Z", "created_at": "2025-08-01T00:05:14.754792448Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1439,7 +1439,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:35.552405157Z", "created_at": "2025-08-01T00:05:15.157906888Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1461,7 +1461,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:35.928893645Z", "created_at": "2025-08-01T00:05:15.567665265Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1483,7 +1483,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:36.307086641Z", "created_at": "2025-08-01T00:05:15.981925795Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1505,7 +1505,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:36.675663437Z", "created_at": "2025-08-01T00:05:16.388785931Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1527,7 +1527,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:37.047857232Z", "created_at": "2025-08-01T00:05:16.795150512Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1549,7 +1549,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:37.416299637Z", "created_at": "2025-08-01T00:05:17.204509535Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1571,7 +1571,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:37.785786223Z", "created_at": "2025-08-01T00:05:17.613690212Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1593,7 +1593,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:38.143189585Z", "created_at": "2025-08-01T00:05:18.020711094Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1615,7 +1615,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:38.52253153Z", "created_at": "2025-08-01T00:05:18.428597263Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1637,7 +1637,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:38.878479077Z", "created_at": "2025-08-01T00:05:18.836863657Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1659,7 +1659,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:39.236508581Z", "created_at": "2025-08-01T00:05:19.248527489Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1681,7 +1681,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:39.598374943Z", "created_at": "2025-08-01T00:05:19.662063245Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1703,7 +1703,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:39.962586339Z", "created_at": "2025-08-01T00:05:20.074553793Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1725,7 +1725,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:40.353257751Z", "created_at": "2025-08-01T00:05:20.494386446Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1747,7 +1747,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:40.721819096Z", "created_at": "2025-08-01T00:05:20.905809772Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1769,7 +1769,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:41.08041087Z", "created_at": "2025-08-01T00:05:21.32374153Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1791,7 +1791,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:41.442236982Z", "created_at": "2025-08-01T00:05:21.732533121Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1813,7 +1813,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:41.799804132Z", "created_at": "2025-08-01T00:05:22.140888939Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1835,7 +1835,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:42.166527887Z", "created_at": "2025-08-01T00:05:22.552257821Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1857,7 +1857,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:42.527814382Z", "created_at": "2025-08-01T00:05:22.970740344Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1879,7 +1879,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:42.895100966Z", "created_at": "2025-08-01T00:05:23.380926627Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1901,7 +1901,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:43.258686863Z", "created_at": "2025-08-01T00:05:23.790553354Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1923,7 +1923,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:43.619567501Z", "created_at": "2025-08-01T00:05:24.202112923Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1945,7 +1945,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:43.976895586Z", "created_at": "2025-08-01T00:05:24.612103888Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1967,7 +1967,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:44.332362908Z", "created_at": "2025-08-01T00:05:25.019727418Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -1989,7 +1989,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:44.690519899Z", "created_at": "2025-08-01T00:05:25.422980466Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -2011,7 +2011,7 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:45.059572858Z", "created_at": "2025-08-01T00:05:25.815598412Z",
"done": false, "done": false,
"done_reason": null, "done_reason": null,
"total_duration": null, "total_duration": null,
@ -2033,15 +2033,15 @@
"__type__": "ollama._types.ChatResponse", "__type__": "ollama._types.ChatResponse",
"__data__": { "__data__": {
"model": "llama3.2-vision:11b", "model": "llama3.2-vision:11b",
"created_at": "2025-07-31T18:04:45.471945315Z", "created_at": "2025-08-01T00:05:26.224081261Z",
"done": true, "done": true,
"done_reason": "stop", "done_reason": "stop",
"total_duration": 34274694911, "total_duration": 37514337521,
"load_duration": 57572534, "load_duration": 60023634,
"prompt_eval_count": 18, "prompt_eval_count": 18,
"prompt_eval_duration": 529292721, "prompt_eval_duration": 561160541,
"eval_count": 92, "eval_count": 92,
"eval_duration": 33679338548, "eval_duration": 36885221241,
"message": { "message": {
"role": "assistant", "role": "assistant",
"content": "", "content": "",

View file

@ -31,6 +31,7 @@ def skip_if_provider_doesnt_support_openai_vector_stores(client_with_models):
"remote::chromadb", "remote::chromadb",
"remote::qdrant", "remote::qdrant",
"inline::qdrant", "inline::qdrant",
"remote::weaviate",
]: ]:
return return
@ -111,11 +112,11 @@ def test_openai_create_vector_store(compat_client_with_empty_stores, client_with
# Create a vector store # Create a vector store
vector_store = client.vector_stores.create( vector_store = client.vector_stores.create(
name="test_vector_store", metadata={"purpose": "testing", "environment": "integration"} name="Vs_test_vector_store", metadata={"purpose": "testing", "environment": "integration"}
) )
assert vector_store is not None assert vector_store is not None
assert vector_store.name == "test_vector_store" assert vector_store.name == "Vs_test_vector_store"
assert vector_store.object == "vector_store" assert vector_store.object == "vector_store"
assert vector_store.status in ["completed", "in_progress"] assert vector_store.status in ["completed", "in_progress"]
assert vector_store.metadata["purpose"] == "testing" assert vector_store.metadata["purpose"] == "testing"

View file

@ -5,7 +5,7 @@
# the root directory of this source tree. # the root directory of this source tree.
from llama_stack.apis.vector_io import Chunk, ChunkMetadata from llama_stack.apis.vector_io import Chunk, ChunkMetadata
from llama_stack.providers.utils.vector_io.chunk_utils import generate_chunk_id from llama_stack.providers.utils.vector_io.vector_utils import generate_chunk_id
# This test is a unit test for the chunk_utils.py helpers. This should only contain # This test is a unit test for the chunk_utils.py helpers. This should only contain
# tests which are specific to this file. More general (API-level) tests should be placed in # tests which are specific to this file. More general (API-level) tests should be placed in

3957
uv.lock generated

File diff suppressed because it is too large Load diff