|
| 1 | +from llama_index.core import VectorStoreIndex, StorageContext, Document |
| 2 | +from llama_index.core.node_parser import SentenceSplitter |
| 3 | +from llama_index.vector_stores.lancedb import LanceDBVectorStore |
| 4 | +from openai import OpenAI as PerplexityClient |
| 5 | +from llama_index.core.vector_stores import MetadataFilters, MetadataFilter, FilterOperator |
| 6 | +import lancedb |
| 7 | +import pyarrow as pa |
| 8 | +import os |
| 9 | +from datetime import datetime |
| 10 | + |
| 11 | +# Initialize Perplexity Sonar client |
| 12 | +sonar_client = PerplexityClient( |
| 13 | + api_key=os.environ["PERPLEXITY_API_KEY"], |
| 14 | + base_url="https://api.perplexity.ai" |
| 15 | +) |
| 16 | + |
| 17 | +# Define explicit schema matching metadata structure |
| 18 | +schema = pa.schema([ |
| 19 | + pa.field("id", pa.string()), |
| 20 | + pa.field("text", pa.string()), |
| 21 | + pa.field("metadata", pa.map_(pa.string(), pa.string())), # Store metadata as key-value map |
| 22 | + pa.field("embedding", pa.list_(pa.float32(), 768)) # Match your embedding dimension |
| 23 | +]) |
| 24 | + |
| 25 | +# Initialize persistent vector store with clean slate |
| 26 | +lancedb_uri = "./lancedb" |
| 27 | +if os.path.exists(lancedb_uri): |
| 28 | + import shutil |
| 29 | + shutil.rmtree(lancedb_uri) |
| 30 | + |
| 31 | +db = lancedb.connect(lancedb_uri) |
| 32 | +vector_store = LanceDBVectorStore(uri=lancedb_uri, table_name="chat_history") |
| 33 | +storage_context = StorageContext.from_defaults(vector_store=vector_store) |
| 34 | + |
| 35 | +# Configure node parser with metadata support |
| 36 | +node_parser = SentenceSplitter( |
| 37 | + chunk_size=1024, |
| 38 | + chunk_overlap=100, |
| 39 | + include_metadata=True |
| 40 | +) |
| 41 | + |
| 42 | +def initialize_chat_session(): |
| 43 | + """Create new session with proper schema""" |
| 44 | + return VectorStoreIndex( |
| 45 | + [], |
| 46 | + storage_context=storage_context, |
| 47 | + node_parser=node_parser |
| 48 | + ) |
| 49 | + |
| 50 | +def chat_with_persistence(user_query: str, index: VectorStoreIndex): |
| 51 | + # Store user query |
| 52 | + user_doc = Document( |
| 53 | + text=user_query, |
| 54 | + metadata={ |
| 55 | + "role": "user", |
| 56 | + "timestamp": datetime.now().isoformat() |
| 57 | + } |
| 58 | + ) |
| 59 | + index.insert_nodes(node_parser.get_nodes_from_documents([user_doc])) |
| 60 | + |
| 61 | + # Retrieve context nodes |
| 62 | + retriever = index.as_retriever(similarity_top_k=3) |
| 63 | + context_nodes = retriever.retrieve(user_query) |
| 64 | + |
| 65 | + # Ensure context relevance by filtering for recent queries |
| 66 | + context_text = "\n".join([ |
| 67 | + f"{n.metadata['role'].title()}: {n.text}" |
| 68 | + for n in context_nodes if n.metadata["role"] == "user" |
| 69 | + ]) |
| 70 | + |
| 71 | + # Generate Sonar API request |
| 72 | + messages = [ |
| 73 | + { |
| 74 | + "role": "system", |
| 75 | + "content": f"Conversation History:\n{context_text}\n\nAnswer the latest query using this context." |
| 76 | + }, |
| 77 | + {"role": "user", "content": user_query} |
| 78 | + ] |
| 79 | + |
| 80 | + response = sonar_client.chat.completions.create( |
| 81 | + model="sonar-pro", |
| 82 | + messages=messages, |
| 83 | + temperature=0.3 |
| 84 | + ) |
| 85 | + |
| 86 | + assistant_response = response.choices[0].message.content |
| 87 | + |
| 88 | + # Store assistant response |
| 89 | + assistant_doc = Document( |
| 90 | + text=assistant_response, |
| 91 | + metadata={ |
| 92 | + "role": "assistant", |
| 93 | + "timestamp": datetime.now().isoformat() |
| 94 | + } |
| 95 | + ) |
| 96 | + index.insert_nodes(node_parser.get_nodes_from_documents([assistant_doc])) |
| 97 | + |
| 98 | + # Persist conversation state |
| 99 | + storage_context.persist(persist_dir="./chat_store") |
| 100 | + return assistant_response |
| 101 | + |
| 102 | +# Usage |
| 103 | +index = initialize_chat_session() |
| 104 | +print("Response:", chat_with_persistence("What's the current weather in London?", index)) |
| 105 | +print("Follow-up:", chat_with_persistence("What about tomorrow's forecast?", index)) |
0 commit comments