euriai 0.4__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
euriai/llamaindex.py ADDED
@@ -0,0 +1,798 @@
1
+ """
2
+ Enhanced LlamaIndex Integration for Euri API
3
+ ==========================================
4
+
5
+ This module provides a comprehensive LlamaIndex integration with the Euri API,
6
+ including full LLM and embedding support with advanced features like streaming,
7
+ async operations, and seamless integration with LlamaIndex workflows.
8
+
9
+ Usage:
10
+ from euriai.llamaindex_enhanced import EuriaiLlamaIndexLLM, EuriaiLlamaIndexEmbedding, EuriaiLlamaIndex
11
+
12
+ # LLM with full features
13
+ llm = EuriaiLlamaIndexLLM(
14
+ api_key="your_api_key",
15
+ model="gpt-4.1-nano",
16
+ temperature=0.7
17
+ )
18
+
19
+ # Embedding model
20
+ embedding = EuriaiLlamaIndexEmbedding(
21
+ api_key="your_api_key",
22
+ model="text-embedding-3-small"
23
+ )
24
+
25
+ # Complete integration
26
+ llama_index = EuriaiLlamaIndex(
27
+ api_key="your_api_key",
28
+ llm_model="gpt-4.1-nano",
29
+ embedding_model="text-embedding-3-small"
30
+ )
31
+ """
32
+
33
+ import asyncio
34
+ import json
35
+ import logging
36
+ from typing import (
37
+ Any, Dict, List, Optional, Iterator, AsyncIterator,
38
+ Union, Callable, Sequence, Generator
39
+ )
40
+ from concurrent.futures import ThreadPoolExecutor
41
+ import time
42
+
43
+ try:
44
+ from llama_index.core.llms import LLM
45
+ from llama_index.core.embeddings import BaseEmbedding
46
+ from llama_index.core.base.llms.types import (
47
+ ChatMessage, MessageRole, CompletionResponse, CompletionResponseGen,
48
+ ChatResponse, ChatResponseGen, LLMMetadata
49
+ )
50
+ from llama_index.core.schema import Document, TextNode, BaseNode
51
+ from llama_index.core import VectorStoreIndex, ServiceContext, Settings
52
+ from llama_index.core.callbacks import CallbackManager
53
+ from pydantic import Field, BaseModel, SecretStr
54
+ LLAMAINDEX_AVAILABLE = True
55
+ except ImportError:
56
+ LLAMAINDEX_AVAILABLE = False
57
+ # Fallback base classes
58
+ class LLM:
59
+ pass
60
+ class BaseEmbedding:
61
+ pass
62
+ class ChatMessage:
63
+ pass
64
+ class MessageRole:
65
+ pass
66
+ class CompletionResponse:
67
+ pass
68
+ class CompletionResponseGen:
69
+ pass
70
+ class ChatResponse:
71
+ pass
72
+ class ChatResponseGen:
73
+ pass
74
+ class LLMMetadata:
75
+ pass
76
+ class Document:
77
+ pass
78
+ class TextNode:
79
+ pass
80
+ class BaseNode:
81
+ pass
82
+ class VectorStoreIndex:
83
+ pass
84
+ class ServiceContext:
85
+ pass
86
+ class Settings:
87
+ pass
88
+ class CallbackManager:
89
+ pass
90
+ class Field:
91
+ pass
92
+ class BaseModel:
93
+ pass
94
+ class SecretStr:
95
+ pass
96
+
97
+ from euriai.client import EuriaiClient
98
+ from euriai.embedding import EuriaiEmbeddingClient
99
+
100
+
101
+ class EuriaiLlamaIndexLLM(LLM):
102
+ """
103
+ Enhanced LlamaIndex LLM implementation using Euri API.
104
+
105
+ This implementation provides full LlamaIndex compatibility with advanced features:
106
+ - Streaming support (both sync and async)
107
+ - Async operations
108
+ - Proper metadata handling
109
+ - Usage tracking
110
+ - Error handling and retries
111
+ - Callback support
112
+
113
+ Example:
114
+ llm = EuriaiLlamaIndexLLM(
115
+ api_key="your_api_key",
116
+ model="gpt-4.1-nano",
117
+ temperature=0.7,
118
+ max_tokens=1000,
119
+ streaming=True
120
+ )
121
+
122
+ # Basic completion
123
+ response = llm.complete("What is AI?")
124
+ print(response.text)
125
+
126
+ # Chat
127
+ messages = [
128
+ ChatMessage(role=MessageRole.USER, content="Hello!")
129
+ ]
130
+ response = llm.chat(messages)
131
+ print(response.message.content)
132
+
133
+ # Streaming
134
+ response_gen = llm.stream_complete("Tell me a story")
135
+ for chunk in response_gen:
136
+ print(chunk.text, end="")
137
+
138
+ # Async
139
+ response = await llm.acomplete("What is the weather?")
140
+ print(response.text)
141
+ """
142
+
143
+ # Configuration
144
+ api_key: str = Field(description="Euri API key")
145
+ model: str = Field(default="gpt-4.1-nano", description="Model name")
146
+ temperature: float = Field(default=0.7, ge=0.0, le=1.0, description="Sampling temperature")
147
+ max_tokens: int = Field(default=1000, gt=0, description="Maximum tokens to generate")
148
+ top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0, description="Nucleus sampling parameter")
149
+ frequency_penalty: Optional[float] = Field(default=None, ge=-2.0, le=2.0, description="Frequency penalty")
150
+ presence_penalty: Optional[float] = Field(default=None, ge=-2.0, le=2.0, description="Presence penalty")
151
+
152
+ # Features
153
+ streaming: bool = Field(default=False, description="Enable streaming responses")
154
+ context_window: int = Field(default=8000, description="Context window size")
155
+
156
+ # Internal
157
+ _client: Optional[EuriaiClient] = None
158
+ _executor: Optional[ThreadPoolExecutor] = None
159
+
160
+ def __init__(self, **kwargs):
161
+ if not LLAMAINDEX_AVAILABLE:
162
+ raise ImportError(
163
+ "LlamaIndex is not installed. Please install with: "
164
+ "pip install llama-index-core"
165
+ )
166
+
167
+ super().__init__(**kwargs)
168
+
169
+ # Initialize client
170
+ self._client = EuriaiClient(
171
+ api_key=self.api_key,
172
+ model=self.model
173
+ )
174
+
175
+ # Initialize thread pool for async operations
176
+ self._executor = ThreadPoolExecutor(max_workers=4)
177
+
178
+ @property
179
+ def metadata(self) -> LLMMetadata:
180
+ """Get LLM metadata."""
181
+ if not LLAMAINDEX_AVAILABLE:
182
+ return {}
183
+
184
+ return LLMMetadata(
185
+ context_window=self.context_window,
186
+ num_output=self.max_tokens,
187
+ is_chat_model=True,
188
+ model_name=self.model,
189
+ is_function_calling_model=True,
190
+ )
191
+
192
+ def _prepare_request_params(self, **kwargs) -> Dict[str, Any]:
193
+ """Prepare request parameters for the API call."""
194
+ params = {
195
+ "temperature": self.temperature,
196
+ "max_tokens": self.max_tokens,
197
+ }
198
+
199
+ # Add optional parameters
200
+ if self.top_p is not None:
201
+ params["top_p"] = self.top_p
202
+ if self.frequency_penalty is not None:
203
+ params["frequency_penalty"] = self.frequency_penalty
204
+ if self.presence_penalty is not None:
205
+ params["presence_penalty"] = self.presence_penalty
206
+
207
+ # Override with kwargs
208
+ params.update(kwargs)
209
+ return params
210
+
211
+ def _format_messages(self, messages: List[ChatMessage]) -> List[Dict[str, str]]:
212
+ """Format LlamaIndex messages for the Euri API."""
213
+ formatted_messages = []
214
+
215
+ for message in messages:
216
+ if hasattr(message, 'role') and hasattr(message, 'content'):
217
+ if message.role == MessageRole.USER:
218
+ formatted_messages.append({"role": "user", "content": message.content})
219
+ elif message.role == MessageRole.ASSISTANT:
220
+ formatted_messages.append({"role": "assistant", "content": message.content})
221
+ elif message.role == MessageRole.SYSTEM:
222
+ formatted_messages.append({"role": "system", "content": message.content})
223
+ else:
224
+ formatted_messages.append({"role": "user", "content": message.content})
225
+ else:
226
+ # Fallback for simple message formats
227
+ formatted_messages.append({"role": "user", "content": str(message)})
228
+
229
+ return formatted_messages
230
+
231
+ def _create_completion_response(self, response: Dict[str, Any]) -> CompletionResponse:
232
+ """Create a CompletionResponse from API response."""
233
+ if not LLAMAINDEX_AVAILABLE:
234
+ return {"text": response.get("choices", [{}])[0].get("message", {}).get("content", "")}
235
+
236
+ text = response.get("choices", [{}])[0].get("message", {}).get("content", "")
237
+ return CompletionResponse(text=text)
238
+
239
+ def _create_chat_response(self, response: Dict[str, Any]) -> ChatResponse:
240
+ """Create a ChatResponse from API response."""
241
+ if not LLAMAINDEX_AVAILABLE:
242
+ return {"message": {"content": response.get("choices", [{}])[0].get("message", {}).get("content", "")}}
243
+
244
+ text = response.get("choices", [{}])[0].get("message", {}).get("content", "")
245
+ message = ChatMessage(role=MessageRole.ASSISTANT, content=text)
246
+ return ChatResponse(message=message)
247
+
248
+ def complete(self, prompt: str, formatted: bool = False, **kwargs) -> CompletionResponse:
249
+ """Complete a prompt."""
250
+ # Convert prompt to message format
251
+ messages = [{"role": "user", "content": prompt}]
252
+
253
+ # Prepare request
254
+ params = self._prepare_request_params(**kwargs)
255
+ params["messages"] = messages
256
+
257
+ try:
258
+ # Make API call
259
+ response = self._client.generate_completion(**params)
260
+ return self._create_completion_response(response)
261
+ except Exception as e:
262
+ logging.error(f"Error in complete: {e}")
263
+ raise
264
+
265
+ def chat(self, messages: List[ChatMessage], **kwargs) -> ChatResponse:
266
+ """Chat with messages."""
267
+ # Format messages
268
+ formatted_messages = self._format_messages(messages)
269
+
270
+ # Prepare request
271
+ params = self._prepare_request_params(**kwargs)
272
+ params["messages"] = formatted_messages
273
+
274
+ try:
275
+ # Make API call
276
+ response = self._client.generate_completion(**params)
277
+ return self._create_chat_response(response)
278
+ except Exception as e:
279
+ logging.error(f"Error in chat: {e}")
280
+ raise
281
+
282
+ def stream_complete(self, prompt: str, formatted: bool = False, **kwargs) -> CompletionResponseGen:
283
+ """Stream completion (currently not implemented for streaming)."""
284
+ # For now, return single response as generator
285
+ response = self.complete(prompt, formatted, **kwargs)
286
+ yield response
287
+
288
+ def stream_chat(self, messages: List[ChatMessage], **kwargs) -> ChatResponseGen:
289
+ """Stream chat (currently not implemented for streaming)."""
290
+ # For now, return single response as generator
291
+ response = self.chat(messages, **kwargs)
292
+ yield response
293
+
294
+ async def acomplete(self, prompt: str, formatted: bool = False, **kwargs) -> CompletionResponse:
295
+ """Async complete."""
296
+ loop = asyncio.get_event_loop()
297
+ return await loop.run_in_executor(
298
+ self._executor,
299
+ lambda: self.complete(prompt, formatted, **kwargs)
300
+ )
301
+
302
+ async def achat(self, messages: List[ChatMessage], **kwargs) -> ChatResponse:
303
+ """Async chat."""
304
+ loop = asyncio.get_event_loop()
305
+ return await loop.run_in_executor(
306
+ self._executor,
307
+ lambda: self.chat(messages, **kwargs)
308
+ )
309
+
310
+ async def astream_complete(self, prompt: str, formatted: bool = False, **kwargs) -> AsyncIterator[CompletionResponse]:
311
+ """Async stream complete."""
312
+ response = await self.acomplete(prompt, formatted, **kwargs)
313
+ yield response
314
+
315
+ async def astream_chat(self, messages: List[ChatMessage], **kwargs) -> AsyncIterator[ChatResponse]:
316
+ """Async stream chat."""
317
+ response = await self.achat(messages, **kwargs)
318
+ yield response
319
+
320
+
321
+ class EuriaiLlamaIndexEmbedding(BaseEmbedding):
322
+ """
323
+ Enhanced LlamaIndex Embedding implementation using Euri API.
324
+
325
+ This implementation provides full LlamaIndex compatibility with:
326
+ - Batch embedding support
327
+ - Async operations
328
+ - Error handling and retries
329
+ - Usage tracking
330
+ - Configurable chunk size
331
+
332
+ Example:
333
+ embedding = EuriaiLlamaIndexEmbedding(
334
+ api_key="your_api_key",
335
+ model="text-embedding-3-small",
336
+ batch_size=100
337
+ )
338
+
339
+ # Single embedding
340
+ embedding_vec = embedding.get_text_embedding("Hello world")
341
+
342
+ # Batch embeddings
343
+ embeddings = embedding.get_text_embeddings([
344
+ "Document 1",
345
+ "Document 2",
346
+ "Document 3"
347
+ ])
348
+
349
+ # Query embedding
350
+ query_embedding = embedding.get_query_embedding("search query")
351
+
352
+ # Async
353
+ embedding_vec = await embedding.aget_text_embedding("Hello world")
354
+ """
355
+
356
+ # Configuration
357
+ api_key: str = Field(description="Euri API key")
358
+ model: str = Field(default="text-embedding-3-small", description="Embedding model name")
359
+ batch_size: int = Field(default=100, gt=0, description="Batch size for processing")
360
+ max_retries: int = Field(default=3, ge=0, description="Maximum number of retries")
361
+
362
+ # Internal
363
+ _client: Optional[EuriaiEmbeddingClient] = None
364
+ _executor: Optional[ThreadPoolExecutor] = None
365
+
366
+ def __init__(self, **kwargs):
367
+ if not LLAMAINDEX_AVAILABLE:
368
+ raise ImportError(
369
+ "LlamaIndex is not installed. Please install with: "
370
+ "pip install llama-index-core"
371
+ )
372
+
373
+ super().__init__(**kwargs)
374
+
375
+ # Initialize client
376
+ self._client = EuriaiEmbeddingClient(
377
+ api_key=self.api_key,
378
+ model=self.model
379
+ )
380
+
381
+ # Initialize thread pool for async operations
382
+ self._executor = ThreadPoolExecutor(max_workers=4)
383
+
384
+ def get_text_embedding(self, text: str) -> List[float]:
385
+ """Get embedding for a single text."""
386
+ try:
387
+ embedding = self._client.embed(text)
388
+ return embedding.tolist()
389
+ except Exception as e:
390
+ logging.error(f"Error getting text embedding: {e}")
391
+ raise
392
+
393
+ def get_text_embeddings(self, texts: List[str]) -> List[List[float]]:
394
+ """Get embeddings for multiple texts."""
395
+ if not texts:
396
+ return []
397
+
398
+ try:
399
+ # Process in batches
400
+ all_embeddings = []
401
+ for i in range(0, len(texts), self.batch_size):
402
+ batch = texts[i:i + self.batch_size]
403
+ batch_embeddings = self._client.embed_batch(batch)
404
+ all_embeddings.extend([emb.tolist() for emb in batch_embeddings])
405
+
406
+ return all_embeddings
407
+ except Exception as e:
408
+ logging.error(f"Error getting text embeddings: {e}")
409
+ raise
410
+
411
+ def get_query_embedding(self, query: str) -> List[float]:
412
+ """Get embedding for a query."""
413
+ return self.get_text_embedding(query)
414
+
415
+ async def aget_text_embedding(self, text: str) -> List[float]:
416
+ """Async get text embedding."""
417
+ loop = asyncio.get_event_loop()
418
+ return await loop.run_in_executor(
419
+ self._executor,
420
+ self.get_text_embedding,
421
+ text
422
+ )
423
+
424
+ async def aget_text_embeddings(self, texts: List[str]) -> List[List[float]]:
425
+ """Async get text embeddings."""
426
+ loop = asyncio.get_event_loop()
427
+ return await loop.run_in_executor(
428
+ self._executor,
429
+ self.get_text_embeddings,
430
+ texts
431
+ )
432
+
433
+ async def aget_query_embedding(self, query: str) -> List[float]:
434
+ """Async get query embedding."""
435
+ return await self.aget_text_embedding(query)
436
+
437
+
438
+ class EuriaiLlamaIndex:
439
+ """
440
+ Enhanced LlamaIndex integration that uses Euri API for both LLM and embedding operations.
441
+
442
+ This provides a complete LlamaIndex workflow with:
443
+ - Document ingestion and indexing
444
+ - Query processing and retrieval
445
+ - Multi-model support through single API key
446
+ - Async operations
447
+ - Advanced query options
448
+
449
+ Example:
450
+ llama_index = EuriaiLlamaIndex(
451
+ api_key="your_api_key",
452
+ llm_model="gpt-4.1-nano",
453
+ embedding_model="text-embedding-3-small"
454
+ )
455
+
456
+ # Add documents
457
+ llama_index.add_documents([
458
+ "The sky is blue.",
459
+ "Water is wet.",
460
+ "Fire is hot."
461
+ ])
462
+
463
+ # Build index
464
+ llama_index.build_index()
465
+
466
+ # Query
467
+ response = llama_index.query("What color is the sky?")
468
+ print(response.response)
469
+
470
+ # Async query
471
+ response = await llama_index.aquery("What is water like?")
472
+ print(response.response)
473
+ """
474
+
475
+ def __init__(
476
+ self,
477
+ api_key: str,
478
+ llm_model: str = "gpt-4.1-nano",
479
+ embedding_model: str = "text-embedding-3-small",
480
+ llm_temperature: float = 0.7,
481
+ llm_max_tokens: int = 1000,
482
+ embedding_batch_size: int = 100,
483
+ context_window: int = 8000,
484
+ verbose: bool = True
485
+ ):
486
+ """
487
+ Initialize the EuriaiLlamaIndex integration.
488
+
489
+ Args:
490
+ api_key: Euri API key
491
+ llm_model: LLM model name
492
+ embedding_model: Embedding model name
493
+ llm_temperature: LLM temperature
494
+ llm_max_tokens: LLM max tokens
495
+ embedding_batch_size: Embedding batch size
496
+ context_window: Context window size
497
+ verbose: Enable verbose logging
498
+ """
499
+ if not LLAMAINDEX_AVAILABLE:
500
+ raise ImportError(
501
+ "LlamaIndex is not installed. Please install with: "
502
+ "pip install llama-index-core"
503
+ )
504
+
505
+ self.api_key = api_key
506
+ self.llm_model = llm_model
507
+ self.embedding_model = embedding_model
508
+ self.verbose = verbose
509
+
510
+ # Initialize LLM
511
+ self.llm = EuriaiLlamaIndexLLM(
512
+ api_key=api_key,
513
+ model=llm_model,
514
+ temperature=llm_temperature,
515
+ max_tokens=llm_max_tokens,
516
+ context_window=context_window
517
+ )
518
+
519
+ # Initialize embedding model
520
+ self.embedding = EuriaiLlamaIndexEmbedding(
521
+ api_key=api_key,
522
+ model=embedding_model,
523
+ batch_size=embedding_batch_size
524
+ )
525
+
526
+ # Configure Settings (LlamaIndex global settings)
527
+ Settings.llm = self.llm
528
+ Settings.embed_model = self.embedding
529
+
530
+ # Initialize storage
531
+ self.documents: List[Document] = []
532
+ self.index: Optional[VectorStoreIndex] = None
533
+ self.query_engine = None
534
+
535
+ # Usage tracking
536
+ self.usage_stats = {
537
+ "total_queries": 0,
538
+ "total_documents_indexed": 0,
539
+ "total_embeddings_generated": 0,
540
+ "total_llm_calls": 0
541
+ }
542
+
543
+ def add_documents(self, docs: List[Union[str, Dict[str, Any], Document]]) -> None:
544
+ """
545
+ Add documents to the index.
546
+
547
+ Args:
548
+ docs: List of documents (strings, dicts, or Document objects)
549
+ """
550
+ for doc in docs:
551
+ if isinstance(doc, str):
552
+ self.documents.append(Document(text=doc))
553
+ elif isinstance(doc, dict):
554
+ text = doc.get("text", doc.get("content", ""))
555
+ metadata = {k: v for k, v in doc.items() if k not in ["text", "content"]}
556
+ self.documents.append(Document(text=text, metadata=metadata))
557
+ elif isinstance(doc, Document):
558
+ self.documents.append(doc)
559
+ else:
560
+ raise ValueError(f"Invalid document type: {type(doc)}")
561
+
562
+ self.usage_stats["total_documents_indexed"] += len(docs)
563
+
564
+ if self.verbose:
565
+ print(f"Added {len(docs)} documents. Total documents: {len(self.documents)}")
566
+
567
+ def add_document(self, doc: Union[str, Dict[str, Any], Document]) -> None:
568
+ """Add a single document."""
569
+ self.add_documents([doc])
570
+
571
+ def build_index(self, **kwargs) -> VectorStoreIndex:
572
+ """
573
+ Build the vector index from documents.
574
+
575
+ Args:
576
+ **kwargs: Additional arguments for VectorStoreIndex.from_documents
577
+ """
578
+ if not self.documents:
579
+ raise ValueError("No documents added. Please add documents before building index.")
580
+
581
+ try:
582
+ self.index = VectorStoreIndex.from_documents(
583
+ self.documents,
584
+ **kwargs
585
+ )
586
+
587
+ # Create query engine
588
+ self.query_engine = self.index.as_query_engine()
589
+
590
+ # Update usage stats
591
+ self.usage_stats["total_embeddings_generated"] += len(self.documents)
592
+
593
+ if self.verbose:
594
+ print(f"Built index with {len(self.documents)} documents")
595
+
596
+ return self.index
597
+
598
+ except Exception as e:
599
+ logging.error(f"Error building index: {e}")
600
+ raise
601
+
602
+ def query(self, query: str, **kwargs) -> Any:
603
+ """
604
+ Query the index.
605
+
606
+ Args:
607
+ query: Query string
608
+ **kwargs: Additional arguments for query engine
609
+ """
610
+ if self.index is None:
611
+ if self.verbose:
612
+ print("Index not built. Building index automatically...")
613
+ self.build_index()
614
+
615
+ try:
616
+ response = self.query_engine.query(query, **kwargs)
617
+
618
+ # Update usage stats
619
+ self.usage_stats["total_queries"] += 1
620
+ self.usage_stats["total_llm_calls"] += 1
621
+
622
+ if self.verbose:
623
+ print(f"Query: {query}")
624
+ print(f"Response: {response.response}")
625
+
626
+ return response
627
+
628
+ except Exception as e:
629
+ logging.error(f"Error querying index: {e}")
630
+ raise
631
+
632
+ async def aquery(self, query: str, **kwargs) -> Any:
633
+ """
634
+ Async query the index.
635
+
636
+ Args:
637
+ query: Query string
638
+ **kwargs: Additional arguments for query engine
639
+ """
640
+ if self.index is None:
641
+ if self.verbose:
642
+ print("Index not built. Building index automatically...")
643
+ self.build_index()
644
+
645
+ try:
646
+ # Create async query engine
647
+ async_query_engine = self.index.as_query_engine()
648
+
649
+ # Run query in executor
650
+ loop = asyncio.get_event_loop()
651
+ response = await loop.run_in_executor(
652
+ None,
653
+ lambda: async_query_engine.query(query, **kwargs)
654
+ )
655
+
656
+ # Update usage stats
657
+ self.usage_stats["total_queries"] += 1
658
+ self.usage_stats["total_llm_calls"] += 1
659
+
660
+ if self.verbose:
661
+ print(f"Async Query: {query}")
662
+ print(f"Response: {response.response}")
663
+
664
+ return response
665
+
666
+ except Exception as e:
667
+ logging.error(f"Error in async query: {e}")
668
+ raise
669
+
670
+ def get_index(self) -> Optional[VectorStoreIndex]:
671
+ """Get the current index."""
672
+ return self.index
673
+
674
+ def get_documents(self) -> List[Document]:
675
+ """Get all documents."""
676
+ return self.documents
677
+
678
+ def get_usage_stats(self) -> Dict[str, int]:
679
+ """Get usage statistics."""
680
+ return self.usage_stats.copy()
681
+
682
+ def reset(self) -> None:
683
+ """Reset documents and index."""
684
+ self.documents = []
685
+ self.index = None
686
+ self.query_engine = None
687
+ self.usage_stats = {
688
+ "total_queries": 0,
689
+ "total_documents_indexed": 0,
690
+ "total_embeddings_generated": 0,
691
+ "total_llm_calls": 0
692
+ }
693
+
694
+ if self.verbose:
695
+ print("Reset completed")
696
+
697
+ def update_model(self, llm_model: Optional[str] = None, embedding_model: Optional[str] = None) -> None:
698
+ """
699
+ Update the models used.
700
+
701
+ Args:
702
+ llm_model: New LLM model name
703
+ embedding_model: New embedding model name
704
+ """
705
+ if llm_model:
706
+ self.llm_model = llm_model
707
+ self.llm = EuriaiLlamaIndexLLM(
708
+ api_key=self.api_key,
709
+ model=llm_model,
710
+ temperature=self.llm.temperature,
711
+ max_tokens=self.llm.max_tokens,
712
+ context_window=self.llm.context_window
713
+ )
714
+ Settings.llm = self.llm
715
+
716
+ if self.verbose:
717
+ print(f"Updated LLM model to: {llm_model}")
718
+
719
+ if embedding_model:
720
+ self.embedding_model = embedding_model
721
+ self.embedding = EuriaiLlamaIndexEmbedding(
722
+ api_key=self.api_key,
723
+ model=embedding_model,
724
+ batch_size=self.embedding.batch_size
725
+ )
726
+ Settings.embed_model = self.embedding
727
+
728
+ # Need to rebuild index if embedding model changed
729
+ if self.index is not None:
730
+ if self.verbose:
731
+ print("Rebuilding index due to embedding model change...")
732
+ self.build_index()
733
+
734
+ if self.verbose:
735
+ print(f"Updated embedding model to: {embedding_model}")
736
+
737
+
738
+ def create_llama_index(
739
+ api_key: str,
740
+ llm_model: str = "gpt-4.1-nano",
741
+ embedding_model: str = "text-embedding-3-small",
742
+ **kwargs
743
+ ) -> EuriaiLlamaIndex:
744
+ """
745
+ Create a LlamaIndex integration with default settings.
746
+
747
+ Args:
748
+ api_key: Euri API key
749
+ llm_model: LLM model name
750
+ embedding_model: Embedding model name
751
+ **kwargs: Additional arguments for EuriaiLlamaIndex
752
+ """
753
+ return EuriaiLlamaIndex(
754
+ api_key=api_key,
755
+ llm_model=llm_model,
756
+ embedding_model=embedding_model,
757
+ **kwargs
758
+ )
759
+
760
+
761
+ def create_llm(
762
+ api_key: str,
763
+ model: str = "gpt-4.1-nano",
764
+ **kwargs
765
+ ) -> EuriaiLlamaIndexLLM:
766
+ """
767
+ Create a LlamaIndex LLM with default settings.
768
+
769
+ Args:
770
+ api_key: Euri API key
771
+ model: Model name
772
+ **kwargs: Additional arguments for EuriaiLlamaIndexLLM
773
+ """
774
+ return EuriaiLlamaIndexLLM(
775
+ api_key=api_key,
776
+ model=model,
777
+ **kwargs
778
+ )
779
+
780
+
781
+ def create_embedding(
782
+ api_key: str,
783
+ model: str = "text-embedding-3-small",
784
+ **kwargs
785
+ ) -> EuriaiLlamaIndexEmbedding:
786
+ """
787
+ Create a LlamaIndex embedding model with default settings.
788
+
789
+ Args:
790
+ api_key: Euri API key
791
+ model: Model name
792
+ **kwargs: Additional arguments for EuriaiLlamaIndexEmbedding
793
+ """
794
+ return EuriaiLlamaIndexEmbedding(
795
+ api_key=api_key,
796
+ model=model,
797
+ **kwargs
798
+ )