2
2
from datetime import datetime
3
3
import json
4
4
from typing import AsyncGenerator , List , Literal , Optional , Dict , Any , Callable
5
+ import uuid
5
6
from pydantic import BaseModel
6
7
from motor .motor_asyncio import AsyncIOMotorClient
7
8
from openai import OpenAI
14
15
from zep_cloud .client import Zep
15
16
from zep_cloud .types import Message , RoleType
16
17
import pandas as pd
18
+ from pinecone import Pinecone
17
19
18
20
19
21
class EventHandler (AssistantEventHandler ):
@@ -73,8 +75,13 @@ def __init__(
73
75
perplexity_api_key : str = None ,
74
76
grok_api_key : str = None ,
75
77
gemini_api_key : str = None ,
78
+ pinecone_api_key : str = None ,
79
+ pinecone_index_name : str = None ,
76
80
code_interpreter : bool = True ,
77
- model : Literal ["gpt-4o-mini" , "gpt-4o" ] = "gpt-4o-mini" ,
81
+ openai_assistant_model : Literal ["gpt-4o-mini" ,
82
+ "gpt-4o" ] = "gpt-4o-mini" ,
83
+ openai_embedding_model : Literal ["text-embedding-3-small" ,
84
+ "text-embedding-3-large" ] = "text-embedding-3-small"
78
85
):
79
86
"""Initialize a new AI assistant with memory and tool integration capabilities.
80
87
@@ -87,8 +94,11 @@ def __init__(
87
94
perplexity_api_key (str, optional): API key for Perplexity search. Defaults to None
88
95
grok_api_key (str, optional): API key for X/Twitter search via Grok. Defaults to None
89
96
gemini_api_key (str, optional): API key for Google Gemini. Defaults to None
97
+ pinecone_api_key (str, optional): API key for Pinecone. Defaults to None
98
+ pinecone_index_name (str, optional): Pinecone index name. Defaults to None
90
99
code_interpreter (bool, optional): Enable code interpretation. Defaults to True
91
- model (Literal["gpt-4o-mini", "gpt-4o"], optional): AI model to use. Defaults to "gpt-4o-mini"
100
+ openai_assistant_model (Literal["gpt-4o-mini", "gpt-4o"], optional): OpenAI model for assistant. Defaults to "gpt-4o-mini"
101
+ openai_embedding_model (Literal["text-embedding-3-small", "text-embedding-3-large"], optional): OpenAI model for text embedding. Defaults to "text-embedding-3-small"
92
102
93
103
Example:
94
104
```python
@@ -99,11 +109,18 @@ def __init__(
99
109
database=MongoDatabase("mongodb://localhost", "ai_db"),
100
110
)
101
111
```
112
+ Notes:
113
+ - Requires valid OpenAI API key for core functionality
114
+ - Database instance for storing messages and threads
115
+ - Optional integrations for Zep, Perplexity, Grok, Gemini, Pinecone, and Cohere
116
+ - Supports code interpretation and custom tool functions
117
+ - You must create the Pinecone index in the dashboard before using it
102
118
"""
103
119
self ._client = OpenAI (api_key = openai_api_key )
104
120
self ._name = name
105
121
self ._instructions = instructions
106
- self ._model = model
122
+ self ._openai_assistant_model = openai_assistant_model
123
+ self ._openai_embedding_model = openai_embedding_model
107
124
self ._tools = [{"type" : "code_interpreter" }
108
125
] if code_interpreter else []
109
126
self ._tool_handlers = {}
@@ -121,6 +138,11 @@ def __init__(
121
138
self ._perplexity_api_key = perplexity_api_key
122
139
self ._grok_api_key = grok_api_key
123
140
self ._gemini_api_key = gemini_api_key
141
+ self ._pinecone = Pinecone (
142
+ api_key = pinecone_api_key ) if pinecone_api_key else None
143
+ self ._pinecone_index_name = pinecone_index_name if pinecone_index_name else None
144
+ self ._pinecone_index = self ._pinecone .Index (
145
+ self ._pinecone_index_name ) if self ._pinecone else None
124
146
125
147
async def __aenter__ (self ):
126
148
assistants = self ._client .beta .assistants .list ()
@@ -134,7 +156,7 @@ async def __aenter__(self):
134
156
name = self .name ,
135
157
instructions = self ._instructions ,
136
158
tools = self ._tools ,
137
- model = self ._model ,
159
+ model = self ._openai_assistant_model ,
138
160
).id
139
161
await self ._database .delete_all_threads ()
140
162
@@ -200,6 +222,81 @@ def csv_to_json(self, file_path: str) -> str:
200
222
records = df .to_dict (orient = "records" )
201
223
return json .dumps (records )
202
224
225
+ # search kb tool - has to be sync
226
+ def search_kb (self , query : str , limit : int = 10 ) -> str :
227
+ """Search Pinecone knowledge base using OpenAI embeddings.
228
+
229
+ Args:
230
+ query (str): Search query to find relevant documents
231
+ limit (int, optional): Maximum number of results to return. Defaults to 10.
232
+
233
+ Returns:
234
+ str: JSON string of matched documents or error message
235
+
236
+ Example:
237
+ ```python
238
+ results = ai.search_kb("machine learning basics", limit=5)
239
+ # Returns: '[{"title": "ML Intro", "content": "..."}]'
240
+ ```
241
+
242
+ Note:
243
+ - Requires configured Pinecone index
244
+ - Uses OpenAI embeddings for semantic search
245
+ - Returns JSON-serialized Pinecone match metadata results
246
+ - Returns error message string if search fails
247
+ """
248
+ try :
249
+ response = self ._client .embeddings .create (
250
+ input = query ,
251
+ model = self ._openai_embedding_model ,
252
+ )
253
+ search_results = self ._pinecone_index .query (
254
+ vector = response .data [0 ].embedding , top_k = limit , include_metadata = True , include_values = False )
255
+ matches = search_results .matches
256
+ metadata = [match .metadata for match in matches ]
257
+ return json .dumps (metadata )
258
+ except Exception as e :
259
+ return f"Failed to search KB. Error: { e } "
260
+
261
+ # add document to kb tool - has to be sync
262
+ def add_document_to_kb (self , document : Dict [str , str ]):
263
+ """Add a document to the Pinecone knowledge base with OpenAI embeddings.
264
+
265
+ Args:
266
+ document (Dict[str, str]): Document to add, with string fields as values
267
+
268
+ Example:
269
+ ```python
270
+ ai.add_document_to_kb({
271
+ "title": "AI Basics",
272
+ "content": "Introduction to artificial intelligence...",
273
+ "author": "John Doe"
274
+ })
275
+ ```
276
+
277
+ Note:
278
+ - Requires Pinecone index to be configured
279
+ - Uses OpenAI embeddings API
280
+ - Document values must be strings
281
+ - Automatically generates UUID for document
282
+ """
283
+ values : List [str ] = []
284
+ for _ , v in document .items ():
285
+ values .append (v )
286
+ response = self ._client .embeddings .create (
287
+ input = values ,
288
+ model = self ._openai_embedding_model ,
289
+ )
290
+ self ._pinecone_index .upsert (
291
+ vectors = [
292
+ {
293
+ "id" : uuid .uuid4 ().hex ,
294
+ "values" : response .data [0 ].embedding ,
295
+ "metadata" : document ,
296
+ }
297
+ ]
298
+ )
299
+
203
300
# summarize tool - has to be sync
204
301
def summarize (
205
302
self , text : str , model : Literal ["gemini-2.0-flash" , "gemini-1.5-pro" ] = "gemini-1.5-pro"
@@ -368,6 +465,7 @@ def reason(
368
465
use_perplexity : bool = True ,
369
466
use_grok : bool = True ,
370
467
use_facts : bool = True ,
468
+ use_kb = True ,
371
469
perplexity_model : Literal [
372
470
"sonar" , "sonar-pro" , "sonar-reasoning-pro" , "sonar-reasoning"
373
471
] = "sonar" ,
@@ -382,6 +480,7 @@ def reason(
382
480
use_perplexity (bool, optional): Include Perplexity search results. Defaults to True
383
481
use_grok (bool, optional): Include X/Twitter search results. Defaults to True
384
482
use_facts (bool, optional): Include stored conversation facts. Defaults to True
483
+ use_kb (bool, optional): Include Pinecone knowledge base search results. Defaults to True
385
484
perplexity_model (Literal, optional): Perplexity model to use. Defaults to "sonar"
386
485
openai_model (Literal, optional): OpenAI model for reasoning. Defaults to "o3-mini"
387
486
grok_model (Literal, optional): Grok model for X search. Defaults to "grok-beta"
@@ -394,9 +493,6 @@ def reason(
394
493
result = ai.reason(
395
494
user_id="user123",
396
495
query="What are the latest AI trends?",
397
- use_perplexity=True,
398
- use_grok=True,
399
- use_facts=True
400
496
)
401
497
# Returns: "Based on multiple sources: [comprehensive answer]"
402
498
```
@@ -421,6 +517,10 @@ def reason(
421
517
x_search_results = self .search_x (query , grok_model )
422
518
else :
423
519
x_search_results = ""
520
+ if use_kb :
521
+ kb_results = self .search_kb (query )
522
+ else :
523
+ kb_results = ""
424
524
425
525
response = self ._client .chat .completions .create (
426
526
model = openai_model ,
@@ -431,7 +531,7 @@ def reason(
431
531
},
432
532
{
433
533
"role" : "user" ,
434
- "content" : f"Query: { query } , Facts: { facts } , Internet Search Results: { search_results } , X Search Results: { x_search_results } " ,
534
+ "content" : f"Query: { query } , Facts: { facts } , KB Results: { kb_results } , Internet Search Results: { search_results } , X Search Results: { x_search_results } " ,
435
535
},
436
536
],
437
537
)
0 commit comments