Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/components/store.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Similarity Search Examples
* `Similarity Search with Milvus (RAG)`_
* `Similarity Search with MongoDB (RAG)`_
* `Similarity Search with Neo4j (RAG)`_
* `Similarity Search with OpenSearch (RAG)`_
* `Similarity Search with Pinecone (RAG)`_
* `Similarity Search with Qdrant (RAG)`_
* `Similarity Search with SurrealDB (RAG)`_
Expand Down Expand Up @@ -97,6 +98,7 @@ Supported Stores
* `Milvus`_
* `MongoDB Atlas`_ (requires ``mongodb/mongodb`` as additional dependency)
* `Neo4j`_
* `OpenSearch`_
* `Pinecone`_ (requires ``probots-io/pinecone-php`` as additional dependency)
* `Postgres`_ (requires ``ext-pdo``)
* `Qdrant`_
Expand Down Expand Up @@ -165,6 +167,7 @@ This leads to a store implementing two methods::
.. _`Similarity Search with Milvus (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/milvus.php
.. _`Similarity Search with MongoDB (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/mongodb.php
.. _`Similarity Search with Neo4j (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/neo4j.php
.. _`Similarity Search with OpenSearch (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/opensearch.php
.. _`Similarity Search with Pinecone (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/pinecone.php
.. _`Similarity Search with Symfony Cache (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/cache.php
.. _`Similarity Search with Qdrant (RAG)`: https://github.com/symfony/ai/blob/main/examples/rag/qdrant.php
Expand All @@ -186,6 +189,7 @@ This leads to a store implementing two methods::
.. _`InMemory`: https://www.php.net/manual/en/language.types.array.php
.. _`Qdrant`: https://qdrant.tech/
.. _`Neo4j`: https://neo4j.com/
.. _`OpenSearch`: https://opensearch.org/
.. _`Typesense`: https://typesense.org/
.. _`Symfony Cache`: https://symfony.com/doc/current/components/cache.html
.. _`Weaviate`: https://weaviate.io/
Expand Down
3 changes: 3 additions & 0 deletions examples/.env
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,6 @@ REDIS_HOST=localhost

# Manticore (store)
MANTICORE_HOST=http://127.0.0.1:9308

# OpenSearch (store)
OPENSEARCH_ENDPOINT=http://127.0.0.1:9200
6 changes: 6 additions & 0 deletions examples/commands/stores.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Symfony\AI\Store\Bridge\Milvus\Store as MilvusStore;
use Symfony\AI\Store\Bridge\MongoDb\Store as MongoDbStore;
use Symfony\AI\Store\Bridge\Neo4j\Store as Neo4jStore;
use Symfony\AI\Store\Bridge\OpenSearch\Store as OpenSearchStore;
use Symfony\AI\Store\Bridge\Postgres\Store as PostgresStore;
use Symfony\AI\Store\Bridge\Qdrant\Store as QdrantStore;
use Symfony\AI\Store\Bridge\Redis\Store as RedisStore;
Expand Down Expand Up @@ -86,6 +87,11 @@
vectorIndexName: 'Commands',
nodeName: 'symfony',
),
'opensearch' => static fn (): OpenSearchStore => new OpenSearchStore(
http_client(),
env('OPENSEARCH_ENDPOINT'),
'symfony',
),
'postgres' => static fn (): PostgresStore => PostgresStore::fromDbal(
DriverManager::getConnection((new DsnParser())->parse(env('POSTGRES_URI'))),
'my_table',
Expand Down
23 changes: 23 additions & 0 deletions examples/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,29 @@ services:
- '7474:7474'
- '7687:7687'

opensearch:
image: opensearchproject/opensearch
environment:
discovery.type: 'single-node'
bootstrap.memory_lock: true
indices.requests.cache.maximum_cacheable_size: 256
DISABLE_SECURITY_PLUGIN: true
OPENSEARCH_JAVA_OPTS: '-Xms512m -Xmx512m'
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
healthcheck:
test: [ 'CMD', 'curl', "-f", "http://127.0.0.1:9200" ]
interval: 30s
retries: 5
ports:
- '9200:9200'
- '9600:9600'

pogocache:
image: pogocache/pogocache
command: [ 'pogocache', '--auth', 'symfony' ]
Expand Down
67 changes: 67 additions & 0 deletions examples/rag/opensearch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Symfony\AI\Agent\Agent;
use Symfony\AI\Agent\Bridge\SimilaritySearch\SimilaritySearch;
use Symfony\AI\Agent\Toolbox\AgentProcessor;
use Symfony\AI\Agent\Toolbox\Toolbox;
use Symfony\AI\Fixtures\Movies;
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;
use Symfony\AI\Store\Bridge\OpenSearch\Store;
use Symfony\AI\Store\Document\Loader\InMemoryLoader;
use Symfony\AI\Store\Document\Metadata;
use Symfony\AI\Store\Document\TextDocument;
use Symfony\AI\Store\Document\Vectorizer;
use Symfony\AI\Store\Indexer;
use Symfony\Component\Uid\Uuid;

require_once dirname(__DIR__).'/bootstrap.php';

// initialize the store
$store = new Store(
httpClient: http_client(),
endpoint: env('OPENSEARCH_ENDPOINT'),
indexName: 'movies',
);

// create embeddings and documents
$documents = [];
foreach (Movies::all() as $i => $movie) {
$documents[] = new TextDocument(
id: Uuid::v4(),
content: 'Title: '.$movie['title'].\PHP_EOL.'Director: '.$movie['director'].\PHP_EOL.'Description: '.$movie['description'],
metadata: new Metadata($movie),
);
}

// initialize the index
$store->setup();

// create embeddings for documents
$platform = PlatformFactory::create(env('OPENAI_API_KEY'), http_client());
$vectorizer = new Vectorizer($platform, 'text-embedding-3-small', logger());
$indexer = new Indexer(new InMemoryLoader($documents), $vectorizer, $store, logger: logger());
$indexer->index($documents);

$similaritySearch = new SimilaritySearch($vectorizer, $store);
$toolbox = new Toolbox([$similaritySearch], logger: logger());
$processor = new AgentProcessor($toolbox);
$agent = new Agent($platform, 'gpt-4o-mini', [$processor], [$processor]);

$messages = new MessageBag(
Message::forSystem('Please answer all user questions only using SimilaritySearch function.'),
Message::ofUser('Which movie fits the theme of technology?')
);
$result = $agent->call($messages);

echo $result->getContent().\PHP_EOL;
21 changes: 21 additions & 0 deletions src/ai-bundle/config/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,27 @@
->end()
->end()
->end()
->arrayNode('opensearch')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->stringNode('endpoint')->cannotBeEmpty()->end()
->stringNode('index_name')->end()
->stringNode('vectors_field')
->defaultValue('_vectors')
->end()
->integerNode('dimensions')
->defaultValue(1536)
->end()
->stringNode('space_type')
->defaultValue('l2')
->end()
->stringNode('http_client')
->defaultValue('http_client')
->end()
->end()
->end()
->end()
->arrayNode('pinecone')
->useAttributeAsKey('name')
->arrayPrototype()
Expand Down
24 changes: 24 additions & 0 deletions src/ai-bundle/src/AiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
use Symfony\AI\Store\Bridge\Milvus\Store as MilvusStore;
use Symfony\AI\Store\Bridge\MongoDb\Store as MongoDbStore;
use Symfony\AI\Store\Bridge\Neo4j\Store as Neo4jStore;
use Symfony\AI\Store\Bridge\OpenSearch\Store as OpenSearchStore;
use Symfony\AI\Store\Bridge\Pinecone\Store as PineconeStore;
use Symfony\AI\Store\Bridge\Postgres\Store as PostgresStore;
use Symfony\AI\Store\Bridge\Qdrant\Store as QdrantStore;
Expand Down Expand Up @@ -1277,6 +1278,29 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
}
}

if ('opensearch' === $type) {
foreach ($stores as $name => $store) {
$definition = new Definition(OpenSearchStore::class);
$definition
->setLazy(true)
->setArguments([
new Reference($store['http_client']),
$store['endpoint'],
$store['index_name'] ?? $name,
$store['vectors_field'],
$store['dimensions'],
$store['space_type'],
])
->addTag('proxy', ['interface' => StoreInterface::class])
->addTag('proxy', ['interface' => ManagedStoreInterface::class])
->addTag('ai.store');

$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
$container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name);
$container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name);
}
}

if ('pinecone' === $type) {
foreach ($stores as $name => $store) {
$arguments = [
Expand Down
Loading
Loading