semantio 0.0.1__py3-none-any.whl → 0.0.2__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.
semantio/agent.py CHANGED
@@ -20,31 +20,35 @@ import os
20
20
  # Configure logging
21
21
  logging.basicConfig(level=logging.INFO)
22
22
  logger = logging.getLogger(__name__)
23
- class Agent(BaseModel):
23
+
24
+ class Assistant(BaseModel):
24
25
  # -*- Agent settings
25
- name: Optional[str] = Field(None, description="Name of the agent.")
26
- description: Optional[str] = Field(None, description="Description of the agent's role.")
27
- instructions: Optional[List[str]] = Field(None, description="List of instructions for the agent.")
26
+ name: Optional[str] = Field(None, description="Name of the assistant.")
27
+ description: Optional[str] = Field(None, description="Description of the assistant's role.")
28
+ instructions: Optional[List[str]] = Field(None, description="List of instructions for the assistant.")
28
29
  model: Optional[str] = Field(None, description="This one is not in the use.")
29
30
  show_tool_calls: bool = Field(False, description="Whether to show tool calls in the response.")
30
31
  markdown: bool = Field(False, description="Whether to format the response in markdown.")
31
- tools: Optional[List[BaseTool]] = Field(None, description="List of tools available to the agent.")
32
- user_name: Optional[str] = Field("User", description="Name of the user interacting with the agent.")
33
- emoji: Optional[str] = Field(":robot:", description="Emoji to represent the agent in the CLI.")
32
+ tools: Optional[List[BaseTool]] = Field(None, description="List of tools available to the assistant.")
33
+ user_name: Optional[str] = Field("User", description="Name of the user interacting with the assistant.")
34
+ emoji: Optional[str] = Field(":robot:", description="Emoji to represent the assistant in the CLI.")
34
35
  rag: Optional[RAG] = Field(None, description="RAG instance for context retrieval.")
35
36
  knowledge_base: Optional[Any] = Field(None, description="Knowledge base for domain-specific information.")
36
37
  llm: Optional[str] = Field(None, description="The LLM provider to use (e.g., 'groq', 'openai', 'anthropic').")
37
38
  llm_model: Optional[str] = Field(None, description="The specific model to use for the LLM provider.")
38
39
  llm_instance: Optional[BaseLLM] = Field(None, description="The LLM instance to use.")
39
40
  json_output: bool = Field(False, description="Whether to format the response as JSON.")
40
- api: bool = Field(False, description="Whether to generate an API for the agent.")
41
+ api: bool = Field(False, description="Whether to generate an API for the assistant.")
41
42
  api_config: Optional[Dict] = Field(
42
43
  None,
43
44
  description="Configuration for the API (e.g., host, port, authentication).",
44
45
  )
45
46
  api_generator: Optional[Any] = Field(None, description="The API generator instance.")
46
47
  expected_output: Optional[Union[str, Dict]] = Field(None, description="The expected format or structure of the output.")
47
- semantic_model: Optional[Any] = Field(None, description="SentenceTransformer model for semantic matching.")
48
+ semantic_model: Optional[Any] = Field(None, description="SentenceTransformer model for semantic matching.")
49
+ team: Optional[List['Assistant']] = Field(None, description="List of assistants in the team.")
50
+ auto_tool: bool = Field(False, description="Whether to automatically detect and call tools.")
51
+
48
52
  # Allow arbitrary types
49
53
  model_config = ConfigDict(arbitrary_types_allowed=True)
50
54
 
@@ -52,8 +56,9 @@ class Agent(BaseModel):
52
56
  super().__init__(**kwargs)
53
57
  # Initialize the model and tools here if needed
54
58
  self._initialize_model()
55
- # Automatically discover and register tools
56
- self.tools = self._discover_tools()
59
+ # Automatically discover and register tools if not provided
60
+ if self.tools is None:
61
+ self.tools = self._discover_tools()
57
62
  # Pass the LLM instance to each tool
58
63
  for tool in self.tools:
59
64
  tool.llm = self.llm_instance
@@ -66,6 +71,41 @@ class Agent(BaseModel):
66
71
  if self.api:
67
72
  self._generate_api()
68
73
 
74
+
75
+ def _generate_response_from_image(self,message: str, image: Union[str, Image], markdown: bool = False, **kwargs) -> str:
76
+ """
77
+ Send the image to the LLM for analysis if the LLM supports vision.
78
+ Supports both local images (PIL.Image) and image URLs.
79
+ """
80
+ try:
81
+ # Check if the LLM supports vision
82
+ if not self.llm_instance or not self.llm_instance.supports_vision:
83
+ raise ValueError("Vision is not supported for the current model.")
84
+ prompt = self._build_prompt(message, context=None)
85
+ # Handle image URL
86
+ if isinstance(image, str) and image.startswith("http"):
87
+ # Directly pass the URL to the LLM
88
+ return self.llm_instance.generate_from_image_url(prompt,image, **kwargs)
89
+
90
+ # Handle local image (PIL.Image)
91
+ elif isinstance(image, Image):
92
+ # Convert the image to bytes
93
+ if image.mode == "RGBA":
94
+ image = image.convert("RGB") # Convert RGBA to RGB
95
+ image_bytes = io.BytesIO()
96
+ image.save(image_bytes, format="JPEG") # Save as PNG (or any supported format)
97
+ image_bytes = image_bytes.getvalue()
98
+
99
+ # Generate response using base64-encoded image bytes
100
+ return self.llm_instance.generate_from_image(prompt,image_bytes, **kwargs)
101
+
102
+ else:
103
+ raise ValueError("Unsupported image type. Provide either a URL or a PIL.Image.")
104
+
105
+ except Exception as e:
106
+ logger.error(f"Failed to generate response from image: {e}")
107
+ return f"An error occurred while processing the image: {e}"
108
+
69
109
  def _discover_tools(self) -> List[BaseTool]:
70
110
  """
71
111
  Automatically discover and register tools from the 'tools' directory.
@@ -122,12 +162,24 @@ class Agent(BaseModel):
122
162
  },
123
163
  "openai": {
124
164
  "class": "OpenAILlm",
125
- "default_model": "gpt-4",
165
+ "default_model": "gpt-4o",
126
166
  },
127
167
  "anthropic": {
128
168
  "class": "AnthropicLlm",
129
169
  "default_model": "claude-2.1",
130
170
  },
171
+ "deepseek": {
172
+ "class": "DeepSeekLLM",
173
+ "default_model": "deepseek-chat",
174
+ },
175
+ "gemini": {
176
+ "class": "GeminiLLM",
177
+ "default_model": "gemini-1.5-flash",
178
+ },
179
+ "mistral": {
180
+ "class": "MistralLLM",
181
+ "default_model": "mistral-large-latest",
182
+ },
131
183
  }
132
184
 
133
185
  # Normalize the LLM provider name (case-insensitive)
@@ -156,23 +208,16 @@ class Agent(BaseModel):
156
208
  retriever = Retriever(vector_store)
157
209
  return RAG(retriever)
158
210
 
159
- def load_image_from_url(self, image_url: str) -> Image:
160
- """Load an image from a URL and return it as a PIL Image."""
161
- response = requests.get(image_url)
162
- image_bytes = response.content
163
- return Image.open(io.BytesIO(image_bytes))
164
-
165
211
  def print_response(
166
212
  self,
167
213
  message: Optional[Union[str, Image, List, Dict]] = None,
168
214
  stream: bool = False,
169
215
  markdown: bool = False,
216
+ tools: Optional[List[BaseTool]] = None,
217
+ team: Optional[List['Assistant']] = None,
170
218
  **kwargs,
171
219
  ) -> Union[str, Dict]: # Add return type hint
172
- """Print the agent's response to the console and return it."""
173
- if isinstance(message, Image):
174
- # Handle image input
175
- message = self._process_image(message)
220
+ """Print the assistant's response to the console and return it."""
176
221
 
177
222
  if stream:
178
223
  # Handle streaming response
@@ -182,26 +227,21 @@ class Agent(BaseModel):
182
227
  response += chunk
183
228
  return response
184
229
  else:
185
- # Generate and return the response
186
- response = self._generate_response(message, markdown=markdown, **kwargs)
230
+ # Generate and return the response
231
+ response = self._generate_response(message, markdown=markdown, tools=tools, team=team, **kwargs)
187
232
  print(response) # Print the response to the console
188
233
  return response
189
234
 
190
- def _process_image(self, image: Image) -> str:
191
- """Process the image and return a string representation."""
192
- # Convert the image to text or extract relevant information
193
- # For now, we'll just return a placeholder string
194
- return "Image processed. Extracted text: [Placeholder]"
195
235
 
196
236
  def _stream_response(self, message: str, markdown: bool = False, **kwargs) -> Iterator[str]:
197
- """Stream the agent's response."""
237
+ """Stream the assistant's response."""
198
238
  # Simulate streaming by yielding chunks of the response
199
239
  response = self._generate_response(message, markdown=markdown, **kwargs)
200
240
  for chunk in response.split():
201
241
  yield chunk + " "
202
242
 
203
243
  def register_tool(self, tool: BaseTool):
204
- """Register a tool for the agent."""
244
+ """Register a tool for the assistant."""
205
245
  if self.tools is None:
206
246
  self.tools = []
207
247
  self.tools.append(tool)
@@ -216,7 +256,7 @@ class Agent(BaseModel):
216
256
 
217
257
  # Create a prompt for the LLM
218
258
  prompt = f"""
219
- You are an AI agent that helps users by selecting the most appropriate tool to answer their query. Below is a list of available tools and their functionalities:
259
+ You are an AI assistant that helps users by selecting the most appropriate tool to answer their query. Below is a list of available tools and their functionalities:
220
260
 
221
261
  {self._get_tool_descriptions()}
222
262
 
@@ -250,7 +290,7 @@ class Agent(BaseModel):
250
290
  """
251
291
  # Create a prompt for the LLM to analyze the query and select tools
252
292
  prompt = f"""
253
- You are an AI agent that helps analyze user queries and select the most appropriate tools.
293
+ You are an AI assistant that helps analyze user queries and select the most appropriate tools.
254
294
  Below is a list of available tools and their functionalities:
255
295
 
256
296
  {self._get_tool_descriptions()}
@@ -284,20 +324,43 @@ class Agent(BaseModel):
284
324
  return []
285
325
 
286
326
 
287
- def _generate_response(self, message: str, markdown: bool = False, **kwargs) -> str:
288
- """Generate the agent's response, including tool execution and context retrieval."""
289
- # Use the LLM to analyze the query and dynamically select tools
290
- tool_calls = self._analyze_query_and_select_tools(message)
291
-
327
+ def _generate_response(self, message: str, markdown: bool = False, tools: Optional[List[BaseTool]] = None, team: Optional[List['Assistant']] = None, **kwargs) -> str:
328
+ """Generate the assistant's response, including tool execution and context retrieval."""
329
+ # Use the specified tools or team if provided
330
+ if tools is not None:
331
+ self.tools = tools
332
+ if team is not None:
333
+ return self._generate_team_response(message, team, markdown=markdown, **kwargs)
334
+
335
+ # Initialize tool_outputs as an empty dictionary
336
+ tool_outputs = {}
292
337
  responses = []
293
- tool_outputs = {} # Store outputs of all tools for collaboration
294
338
 
295
- # Execute tools if any are detected
339
+ # Use the LLM to analyze the query and dynamically select tools when auto_tool is enabled
340
+ if self.auto_tool:
341
+ tool_calls = self._analyze_query_and_select_tools(message)
342
+ else:
343
+ # Check if tools are provided
344
+ if self.tools:
345
+ tool_calls = [
346
+ {
347
+ "tool": tool.__class__.__name__,
348
+ "input": {
349
+ "query": message, # Use the message as the query
350
+ "context": None, # No context provided by default
351
+ }
352
+ }
353
+ for tool in self.tools
354
+ ]
355
+ else:
356
+ tool_calls = kwargs.get("tool_calls", [])
357
+
358
+ # Execute tools if any are detected
296
359
  if tool_calls:
297
360
  for tool_call in tool_calls:
298
361
  tool_name = tool_call["tool"]
299
362
  tool_input = tool_call["input"]
300
-
363
+
301
364
  # Find the tool
302
365
  tool = next((t for t in self.tools if t.name.lower() == tool_name.lower()), None)
303
366
  if tool:
@@ -333,9 +396,8 @@ class Agent(BaseModel):
333
396
  except Exception as e:
334
397
  logger.error(f"Failed to generate LLM response: {e}")
335
398
  responses.append(f"An error occurred while generating the analysis: {e}")
336
-
337
- # If no tools were executed, proceed with the original logic
338
399
  if not tool_calls:
400
+ # If no tools were executed, proceed with the original logic
339
401
  # Retrieve relevant context using RAG
340
402
  rag_context = self.rag.retrieve(message) if self.rag else None
341
403
  # Retrieve relevant context from the knowledge base (API result)
@@ -370,8 +432,15 @@ class Agent(BaseModel):
370
432
  if markdown:
371
433
  return f"**Response:**\n\n{response}"
372
434
  return response
373
-
374
- # Combine all responses into a single output
435
+ # Combine all responses into a single string
436
+ return "\n\n".join(responses)
437
+
438
+ def _generate_team_response(self, message: str, team: List['Assistant'], markdown: bool = False, **kwargs) -> str:
439
+ """Generate a response using a team of assistants."""
440
+ responses = []
441
+ for assistant in team:
442
+ response = assistant.print_response(message, markdown=markdown, **kwargs)
443
+ responses.append(f"**{assistant.name}:**\n\n{response}")
375
444
  return "\n\n".join(responses)
376
445
 
377
446
  def _build_prompt(self, message: str, context: Optional[List[Dict]]) -> str:
@@ -509,7 +578,7 @@ class Agent(BaseModel):
509
578
  exit_on: Optional[List[str]] = None,
510
579
  **kwargs,
511
580
  ):
512
- """Run the agent in a CLI app."""
581
+ """Run the assistant in a CLI app."""
513
582
  from rich.prompt import Prompt
514
583
 
515
584
  if message:
@@ -524,15 +593,15 @@ class Agent(BaseModel):
524
593
  self.print_response(message=message, **kwargs)
525
594
 
526
595
  def _generate_api(self):
527
- """Generate an API for the agent if api=True."""
596
+ """Generate an API for the assistant if api=True."""
528
597
  from .api.api_generator import APIGenerator
529
598
  self.api_generator = APIGenerator(self)
530
- print(f"API generated for agent '{self.name}'. Use `.run_api()` to start the API server.")
599
+ print(f"API generated for assistant '{self.name}'. Use `.run_api()` to start the API server.")
531
600
 
532
601
  def run_api(self):
533
- """Run the API server for the agent."""
602
+ """Run the API server for the assistant."""
534
603
  if not hasattr(self, 'api_generator'):
535
- raise ValueError("API is not enabled for this agent. Set `api=True` when initializing the agent.")
604
+ raise ValueError("API is not enabled for this assistant. Set `api=True` when initializing the assistant.")
536
605
 
537
606
  # Get API configuration
538
607
  host = self.api_config.get("host", "0.0.0.0") if self.api_config else "0.0.0.0"
semantio/llm/__init__.py CHANGED
@@ -1,17 +1,24 @@
1
1
  from .openai import OpenAILlm
2
- from .anthropic import AnthropicLlm
3
- # from .llama import LlamaLlm
2
+ from .anthropic import AnthropicLLM
4
3
  from .groq import GroqLlm
4
+ from .mistral import MistralLLM
5
+ from .deepseek import DeepSeekLLM
6
+ from .gemini import GeminiLLM
5
7
 
6
8
  def get_llm(provider: str, **kwargs):
7
9
  provider = provider.lower() # Convert provider name to lowercase
8
10
  if provider == "openai":
9
11
  return OpenAILlm(**kwargs)
10
12
  elif provider == "anthropic":
11
- return AnthropicLlm(**kwargs)
12
- # elif provider == "llama":
13
- # return LlamaLlm(**kwargs)
13
+ return AnthropicLLM(**kwargs)
14
14
  elif provider == "groq":
15
15
  return GroqLlm(**kwargs)
16
+ elif provider == "mistral":
17
+ return MistralLLM(**kwargs)
18
+ elif provider == "deepseek":
19
+ return DeepSeekLLM(**kwargs)
20
+ elif provider == "gemini":
21
+ return GeminiLLM(**kwargs)
22
+
16
23
  else:
17
24
  raise ValueError(f"Unsupported LLM provider: {provider}")
semantio/llm/anthropic.py CHANGED
@@ -1,39 +1,52 @@
1
+ import os
1
2
  from typing import List, Dict, Optional
2
3
  from .base_llm import BaseLLM
3
4
  import anthropic
4
- import os
5
5
 
6
- class AnthropicLlm(BaseLLM):
7
- def __init__(
8
- self,
9
- model: str = "claude-2.1", # Default Anthropic model
10
- api_key: Optional[str] = None,
11
- ):
6
+ class AnthropicLLM(BaseLLM):
7
+ def __init__(self, model: str = "claude-3-5-sonnet-20241022", api_key: Optional[str] = None):
8
+ """
9
+ Initialize the Anthropic LLM class.
10
+
11
+ Args:
12
+ model (str): The name of the model (e.g., claude-3-5-sonnet-20241022).
13
+ api_key (Optional[str]): The Anthropic API key. If not provided, it fetches from the environment.
14
+ """
12
15
  self.model = model
13
16
  self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
14
17
  if not self.api_key:
15
18
  raise ValueError("Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or pass it explicitly.")
16
- self.client = anthropic.Client(api_key=self.api_key)
19
+ self.client = anthropic.Anthropic(api_key=self.api_key)
20
+
21
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
22
+ """
23
+ Generate text using Anthropic's Claude model.
24
+
25
+ Args:
26
+ prompt (str): The user prompt.
27
+ context (Optional[List[Dict]]): Context to include in the conversation.
28
+ memory (Optional[List[Dict]]): Memory from previous interactions.
17
29
 
18
- def generate(
19
- self,
20
- prompt: str,
21
- context: Optional[List[Dict]] = None,
22
- memory: Optional[List[Dict]] = None,
23
- ) -> str:
24
- # Prepare messages for the Anthropic API
25
- messages = []
26
- if memory:
27
- messages.extend(memory)
28
- if context:
29
- messages.append({"role": "system", "content": "Context: " + str(context)})
30
- messages.append({"role": "user", "content": prompt})
30
+ Returns:
31
+ str: The generated response from the model.
32
+ """
33
+ try:
34
+ # Prepare messages for the Anthropic API
35
+ messages = []
36
+ if memory:
37
+ messages.extend(memory)
38
+ if context:
39
+ messages.append({"role": "system", "content": "Context: " + str(context)})
40
+ messages.append({"role": "user", "content": prompt})
31
41
 
32
- # Call Anthropic API
33
- response = self.client.completion(
34
- model=self.model,
35
- messages=messages,
36
- )
42
+ # Call the Anthropic API
43
+ response = self.client.messages.create(
44
+ model=self.model,
45
+ max_tokens=1024,
46
+ messages=messages,
47
+ )
37
48
 
38
- # Extract and return the response
39
- return response.choices[0].message.content
49
+ # Extract and return the response
50
+ return response.content
51
+ except Exception as e:
52
+ raise ValueError(f"Error while generating response with Anthropic Claude: {e}")
semantio/llm/base_llm.py CHANGED
@@ -9,4 +9,13 @@ class BaseLLM(ABC):
9
9
  context: Optional[List[Dict]] = None,
10
10
  memory: Optional[List[Dict]] = None,
11
11
  ) -> str:
12
- pass
12
+ pass
13
+
14
+ @property
15
+ def supports_vision(self) -> bool:
16
+ """Return True if the LLM supports vision tasks."""
17
+ return False
18
+
19
+ def generate_from_image(self, image_bytes: bytes, **kwargs) -> str:
20
+ """Process an image if vision is supported. Default implementation raises an error."""
21
+ raise NotImplementedError("This LLM does not support vision tasks.")
@@ -0,0 +1,27 @@
1
+ from typing import List, Dict, Optional
2
+ from .base_llm import BaseLLM
3
+ from openai import OpenAI
4
+ import os
5
+
6
+ class DeepSeekLLM(BaseLLM):
7
+ def __init__(self, model: str = "deepseek-chat", api_key: Optional[str] = None):
8
+ self.model = model
9
+ self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY")
10
+ if not self.api_key:
11
+ raise ValueError("DeepSeek API key is required. Set DEEPSEEK_API_KEY environment variable or pass it explicitly.")
12
+ self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
13
+
14
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
15
+ messages = []
16
+ if memory:
17
+ messages.extend(memory)
18
+ if context:
19
+ messages.append({"role": "system", "content": "Context: " + str(context)})
20
+ messages.append({"role": "user", "content": prompt})
21
+
22
+ response = self.client.chat.completions.create(
23
+ model=self.model,
24
+ messages=messages,
25
+ )
26
+
27
+ return response.choices[0].message.content
semantio/llm/gemini.py ADDED
@@ -0,0 +1,50 @@
1
+ import os
2
+ from typing import List, Dict, Optional
3
+ from .base_llm import BaseLLM
4
+ from google import genai
5
+
6
+ class GeminiLLM(BaseLLM):
7
+ def __init__(self, model: str = "gemini-1.5-flash", api_key: Optional[str] = None):
8
+ """
9
+ Initialize the Gemini LLM class.
10
+
11
+ Args:
12
+ model (str): The name of the Gemini model (e.g., 'gemini-1.5-flash').
13
+ api_key (Optional[str]): The Gemini API key. If not provided, it fetches from the environment.
14
+ """
15
+ self.model = model
16
+ self.api_key = api_key or os.getenv("GEMINI_API_KEY")
17
+ if not self.api_key:
18
+ raise ValueError("Gemini API key is required. Set GEMINI_API_KEY environment variable or pass it explicitly.")
19
+
20
+ # Initialize the client using the API key
21
+ self.client = genai.Client(api_key=self.api_key)
22
+
23
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
24
+ """
25
+ Generate text using Google's Gemini model.
26
+
27
+ Args:
28
+ prompt (str): The user prompt.
29
+ context (Optional[List[Dict]]): Context to include in the conversation.
30
+ memory (Optional[List[Dict]]): Memory from previous interactions.
31
+
32
+ Returns:
33
+ str: The generated response from the model.
34
+ """
35
+ try:
36
+ # Prepare the chat history (optional context and memory)
37
+ history = memory if memory else []
38
+ if context:
39
+ history.append({"role": "system", "content": str(context)})
40
+
41
+ # Generate the content using the specified Gemini model
42
+ response = self.client.models.generate_content(
43
+ model=self.model,
44
+ contents=prompt
45
+ )
46
+
47
+ # Return the response text
48
+ return response.text
49
+ except Exception as e:
50
+ raise ValueError(f"Error while generating response with Gemini: {e}")
semantio/llm/groq.py CHANGED
@@ -1,4 +1,5 @@
1
- from typing import List, Dict, Optional
1
+ import base64
2
+ from typing import Optional, List, Dict
2
3
  from .base_llm import BaseLLM
3
4
  import groq
4
5
  import os
@@ -15,12 +16,22 @@ class GroqLlm(BaseLLM):
15
16
  raise ValueError("Groq API key is required. Set GROQ_API_KEY environment variable or pass it explicitly.")
16
17
  self.client = groq.Client(api_key=self.api_key)
17
18
 
18
- def generate(
19
- self,
20
- prompt: str,
21
- context: Optional[List[Dict]] = None,
22
- memory: Optional[List[Dict]] = None,
23
- ) -> str:
19
+ @property
20
+ def supports_vision(self) -> bool:
21
+ """
22
+ Check if the model supports vision tasks.
23
+ """
24
+ # List of Groq models that support vision
25
+ vision_models = [
26
+ "llama-3.2-11b-vision-preview",
27
+ "llama-3.2-90b-vision-preview"
28
+ ]
29
+ return self.model in vision_models
30
+
31
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
32
+ """
33
+ Generate a response to a text-based prompt.
34
+ """
24
35
  # Prepare messages for the Groq API
25
36
  messages = []
26
37
  if memory:
@@ -36,4 +47,75 @@ class GroqLlm(BaseLLM):
36
47
  )
37
48
 
38
49
  # Extract and return the response
39
- return response.choices[0].message.content
50
+ return response.choices[0].message.content
51
+
52
+ def generate_from_image(self, prompt: str, image_bytes: bytes, **kwargs) -> str:
53
+ """
54
+ Process an image and generate a response if the model supports vision.
55
+ """
56
+ if not self.supports_vision:
57
+ raise ValueError(f"Model '{self.model}' does not support vision tasks.")
58
+
59
+ try:
60
+ # Convert the image bytes to base64
61
+ image_base64 = base64.b64encode(image_bytes).decode("utf-8")
62
+
63
+ # Construct the message payload
64
+ messages = [
65
+ {
66
+ "role": "user",
67
+ "content": [
68
+ {"type": "text", "text": prompt},
69
+ {
70
+ "type": "image_url",
71
+ "image_url": {
72
+ "url": f"data:image/jpeg;base64,{image_base64}",
73
+ },
74
+ },
75
+ ],
76
+ }
77
+ ]
78
+
79
+ # Call the Groq API with the base64-encoded image
80
+ response = self.client.chat.completions.create(
81
+ model=self.model,
82
+ messages=messages,
83
+ **kwargs,
84
+ )
85
+
86
+ # Extract and return the response text
87
+ return response.choices[0].message.content
88
+ except Exception as e:
89
+ raise ValueError(f"Error while processing image with Groq vision model: {e}")
90
+
91
+
92
+ def generate_from_image_url(self, prompt: str, image_url: str, **kwargs) -> str:
93
+ """
94
+ Process an image URL and generate a response if the model supports vision.
95
+ """
96
+ if not self.supports_vision:
97
+ raise ValueError(f"Model '{self.model}' does not support vision tasks.")
98
+
99
+ try:
100
+ # Call the Groq API with the image URL
101
+ response = self.client.chat.completions.create(
102
+ model=self.model,
103
+ messages=[
104
+ {
105
+ "role": "user",
106
+ "content": [
107
+ {"type": "text", "text": prompt},
108
+ {
109
+ "type": "image_url",
110
+ "image_url": {
111
+ "url": image_url,
112
+ },
113
+ },
114
+ ],
115
+ }
116
+ ],
117
+ **kwargs,
118
+ )
119
+ return response.choices[0].message.content
120
+ except Exception as e:
121
+ raise ValueError(f"Error while processing image URL with Groq vision model: {e}")
@@ -0,0 +1,27 @@
1
+ from typing import List, Dict, Optional
2
+ from .base_llm import BaseLLM
3
+ from mistralai import Mistral
4
+ import os
5
+
6
+ class MistralLLM(BaseLLM):
7
+ def __init__(self, model: str = "mistral-large-latest", api_key: Optional[str] = None):
8
+ self.model = model
9
+ self.api_key = api_key or os.getenv("MISTRAL_API_KEY")
10
+ if not self.api_key:
11
+ raise ValueError("Mistral API key is required. Set MISTRAL_API_KEY environment variable or pass it explicitly.")
12
+ self.client = Mistral(api_key=self.api_key)
13
+
14
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
15
+ messages = []
16
+ if memory:
17
+ messages.extend(memory)
18
+ if context:
19
+ messages.append({"role": "system", "content": "Context: " + str(context)})
20
+ messages.append({"role": "user", "content": prompt})
21
+
22
+ response = self.client.chat.complete(
23
+ model=self.model,
24
+ messages=messages,
25
+ )
26
+
27
+ return response.choices[0].message.content
semantio/llm/openai.py CHANGED
@@ -1,26 +1,136 @@
1
1
  from typing import List, Dict, Optional
2
2
  from .base_llm import BaseLLM
3
- import openai
3
+ from openai import OpenAI
4
4
  import os
5
+ import base64
5
6
 
6
7
  class OpenAILlm(BaseLLM):
7
- def __init__(self, model: str = "gpt-4", api_key: Optional[str] = None):
8
+ def __init__(self, model: str = "gpt-4o", api_key: Optional[str] = None):
9
+ """
10
+ Initialize the OpenAI LLM class.
11
+
12
+ Args:
13
+ model (str): The name of the model (e.g., gpt-4o, gpt-4-vision).
14
+ api_key (Optional[str]): The OpenAI API key. If not provided, it fetches from the environment.
15
+ """
8
16
  self.model = model
9
17
  self.api_key = api_key or os.getenv("OPENAI_API_KEY")
10
18
  if not self.api_key:
11
19
  raise ValueError("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it explicitly.")
12
- openai.api_key = self.api_key
20
+ self.client = OpenAI(api_key=self.api_key)
13
21
 
14
22
  def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
15
- messages = []
16
- if memory:
17
- messages.extend(memory)
18
- if context:
19
- messages.append({"role": "system", "content": "Context: " + str(context)})
20
- messages.append({"role": "user", "content": prompt})
21
-
22
- response = openai.ChatCompletion.create(
23
- model=self.model,
24
- messages=messages,
25
- )
26
- return response.choices[0].message["content"]
23
+ """
24
+ Generate text using OpenAI's ChatCompletion API.
25
+
26
+ Args:
27
+ prompt (str): The user prompt.
28
+ context (Optional[List[Dict]]): Context to include in the conversation.
29
+ memory (Optional[List[Dict]]): Memory from previous interactions.
30
+
31
+ Returns:
32
+ str: The generated response from the model.
33
+ """
34
+ try:
35
+ # Prepare messages for the OpenAI API
36
+ messages = []
37
+ if memory:
38
+ messages.extend(memory)
39
+ if context:
40
+ messages.append({"role": "system", "content": "Context: " + str(context)})
41
+ messages.append({"role": "user", "content": prompt})
42
+
43
+ # Call the ChatCompletion endpoint
44
+ response = self.client.chat.completions.create(
45
+ model=self.model,
46
+ messages=messages,
47
+ )
48
+
49
+ # Extract and return the response:
50
+ return response.choices[0].message.content
51
+ except Exception as e:
52
+ raise ValueError(f"Error while generating response with OpenAI: {e}")
53
+
54
+ @property
55
+ def supports_vision(self) -> bool:
56
+ """
57
+ Check if the model supports vision tasks.
58
+ """
59
+ # List of GPT models that support vision
60
+ vision_models =[
61
+ "gpt-4o", "gpt-4o mini", "o1", "o1 mini"
62
+ ]
63
+ return self.model in vision_models
64
+
65
+ def generate_from_image_url(self, prompt: str, image_url: str, **kwargs) -> str:
66
+ """
67
+ Process an image URL with OpenAI's vision-capable models, using instructions as the prompt.
68
+
69
+ Args:
70
+ image_url (str): The URL of the image.
71
+ instructions (str): Instructions provided as the prompt for image analysis.
72
+ kwargs: Additional parameters for the OpenAI API.
73
+
74
+ Returns:
75
+ str: The response generated by the vision-capable model.
76
+ """
77
+ if not self.supports_vision:
78
+ raise ValueError(f"Model '{self.model}' does not support vision tasks.")
79
+
80
+ try:
81
+ # Use instructions as the prompt in the API call
82
+ response = self.client.chat.completions.create(
83
+ model=self.model,
84
+ messages=[
85
+ {
86
+ "role": "user",
87
+ "content": [
88
+ {"type": "text", "text": prompt}, # Using instructions as the prompt
89
+ {"type": "image_url", "image_url": {"url": image_url,},},
90
+ ],
91
+ }
92
+ ],
93
+ **kwargs,
94
+ )
95
+ return response.choices[0].message.content
96
+ except Exception as e:
97
+ raise ValueError(f"Error while processing image URL with OpenAI Vision model: {e}")
98
+
99
+ def generate_from_image(self, prompt: str, image_bytes: bytes, **kwargs) -> str:
100
+ """
101
+ Process an image and generate a response if the model supports vision.
102
+ """
103
+ if not self.supports_vision:
104
+ raise ValueError(f"Model '{self.model}' does not support vision tasks.")
105
+
106
+ try:
107
+ # Convert the image bytes to base64
108
+ image_base64 = base64.b64encode(image_bytes).decode("utf-8")
109
+
110
+ # Construct the message payload
111
+ messages = [
112
+ {
113
+ "role": "user",
114
+ "content": [
115
+ {"type": "text", "text": prompt},
116
+ {
117
+ "type": "image_url",
118
+ "image_url": {
119
+ "url": f"data:image/jpeg;base64,{image_base64}",
120
+ },
121
+ },
122
+ ],
123
+ }
124
+ ]
125
+
126
+ # Call the Groq API with the base64-encoded image
127
+ response = self.client.chat.completions.create(
128
+ model=self.model,
129
+ messages=messages,
130
+ **kwargs,
131
+ )
132
+
133
+ # Extract and return the response text
134
+ return response.choices[0].message.content
135
+ except Exception as e:
136
+ raise ValueError(f"Error while processing image with OpenAI vision model: {e}")
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: semantio
3
- Version: 0.0.1
4
- Summary: A powerful SDK for building AI agents with RAG capabilities.
3
+ Version: 0.0.2
4
+ Summary: A powerful SDK for building AI agents
5
5
  Home-page: https://github.com/Syenah/semantio
6
6
  Author: Rakesh
7
7
  Author-email: rakeshsahoo689@gmail.com
@@ -19,7 +19,8 @@ License-File: LICENSE
19
19
  Requires-Dist: openai
20
20
  Requires-Dist: anthropic
21
21
  Requires-Dist: groq
22
- Requires-Dist: langchain
22
+ Requires-Dist: google-genai
23
+ Requires-Dist: mistralai
23
24
  Requires-Dist: faiss-cpu
24
25
  Requires-Dist: pydantic
25
26
  Requires-Dist: requests
@@ -32,8 +33,6 @@ Requires-Dist: sentence-transformers
32
33
  Requires-Dist: fuzzywuzzy
33
34
  Requires-Dist: duckduckgo-search
34
35
  Requires-Dist: yfinance
35
- Requires-Dist: forex-python
36
- Requires-Dist: qrcode
37
36
 
38
37
  # Semantio: The Mother of Your AI Agents
39
38
 
@@ -112,7 +111,9 @@ Semantio/
112
111
  │ │ ├── __init__.py
113
112
  │ │ ├── openai.py # OpenAI integration
114
113
  │ │ ├── anthropic.py # Anthropic (Claude) integration
115
- │ │ ├── llama.py # Llama 2 integration
114
+ │ │ ├── deepseek.py # Deepseek integration
115
+ │ │ ├── gemini.py # Gemini integration
116
+ │ │ ├── mistral.py # Mistral integration
116
117
  │ │ └── base_llm.py # Base class for LLMs
117
118
  │ ├── knowledge_base/ # Knowledge base integration
118
119
  │ │ ├── __init__.py
@@ -1,5 +1,5 @@
1
1
  semantio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- semantio/agent.py,sha256=iS9Yf18iG43SYTxSjmYCZl69OinRKqiVvnBtvLnEF8o,27001
2
+ semantio/agent.py,sha256=mwcKaHy-HY26hxgCRZS5LZw1semlj59qF4uYcLpyqBc,30247
3
3
  semantio/memory.py,sha256=eNAwyAokppHzMcIyFgOw2hT2wnLQBd9GL4T5eallNV4,281
4
4
  semantio/rag.py,sha256=ROy3Pa1NURcDs6qQZ8IMoa5Xlzt6I-msEq0C1p8UgB0,472
5
5
  semantio/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -11,12 +11,15 @@ semantio/knowledge_base/__init__.py,sha256=mvp0GFiGSjcxlkaDulAwKOCL9s6gsKTqhPKXF
11
11
  semantio/knowledge_base/document_loader.py,sha256=nix0yZJ-JJoDbhLkpg5bKDMvNrwykmknI7MRIn0N81k,1910
12
12
  semantio/knowledge_base/retriever.py,sha256=XpdzKS1UCncJImVMtG67VXMC7lp2eRzKnShjvktsFMM,1271
13
13
  semantio/knowledge_base/vector_store.py,sha256=4Zv9kfqDD3cfn_4R8ZoLKdAQCZRYo_IENP_KkLB_RPc,987
14
- semantio/llm/__init__.py,sha256=aZ1vA6qdnz0481Xhi-5GpnxmqyO-5pLYQgww6xYaT-4,572
15
- semantio/llm/anthropic.py,sha256=nGq52klAz_N3foJJRNgUwW9LNpBHmZx-y-EEwR4yh78,1268
16
- semantio/llm/base_llm.py,sha256=YezjFrgAz-2jT7t70EnE3x-OHoWFxauBlhrQxP4RJk0,287
17
- semantio/llm/groq.py,sha256=ZfYoZ5c7oW9GoryJBeKhthqB91zC-LilOVnMoiu9Opg,1244
14
+ semantio/llm/__init__.py,sha256=-4uKcqo9fBrEbvfxGE01XVHL9qEG2vKXfy5hlnUsRbw,779
15
+ semantio/llm/anthropic.py,sha256=-JTso9vr88T3JSipxE60uZjqDgfla1QFoSEBpXW2pXw,2054
16
+ semantio/llm/base_llm.py,sha256=VFl_2S4kqYDuCTWIfWMbKU5aNbVqOCG33E4APOSHF90,668
17
+ semantio/llm/deepseek.py,sha256=oxX-Uw0_lY2sstYs5KGBGFB_hAZUbZomPADdib1mY2M,1100
18
+ semantio/llm/gemini.py,sha256=er3zv1jOvWQBGbPuv4fS4pR_c_abHyhroe-rkXupOO4,1959
19
+ semantio/llm/groq.py,sha256=1AH30paKzDIQjBjWPQPN44QwFHsIOVwI-a587-cDIVc,4285
18
20
  semantio/llm/llama.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- semantio/llm/openai.py,sha256=F0sFYsJv6XdDQFNoG2dkGH8QuD5iE5hQ3-ulGYnLnJc,1016
21
+ semantio/llm/mistral.py,sha256=NpvaB1cE6-jMEBdT0mTf6Ca4Qq2LS8QivDKI6AgdRjE,1061
22
+ semantio/llm/openai.py,sha256=I3ab-d_zFxm-TDhYk6t1PzDtElPJEEQ2eSiARBNIGi4,5174
20
23
  semantio/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
24
  semantio/storage/cloud_storage.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
25
  semantio/storage/local_storage.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -32,9 +35,9 @@ semantio/utils/date_utils.py,sha256=x3oqRGv6ee_KCJ0LvCqqZh_FSgS6YGOHBwZQS4TJetY,
32
35
  semantio/utils/file_utils.py,sha256=b_cMuJINEGk9ikNuNHSn9lsmICWwvtnCDZ03ndH_S2I,1779
33
36
  semantio/utils/logger.py,sha256=TmGbP8BRjLMWjXi2GWzZ0RIXt70x9qX3FuIqghCNlwM,510
34
37
  semantio/utils/validation_utils.py,sha256=iwoxEb4Q5ILqV6tbesMjPWPCCoL3AmPLejGUy6q8YvQ,1284
35
- semantio-0.0.1.dist-info/LICENSE,sha256=teQbWD2Zlcl1_Fo29o2tNbs6G26hbCQiUzds5fQGYlY,1063
36
- semantio-0.0.1.dist-info/METADATA,sha256=XvS9jISWNZ6PeqdbuTRH9heUdfopa2ILmlrnQOSnBZk,6715
37
- semantio-0.0.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
38
- semantio-0.0.1.dist-info/entry_points.txt,sha256=zbPgevSLwcLpdRHqI_atE8EOt8lK2vRF1AoDflDTo18,53
39
- semantio-0.0.1.dist-info/top_level.txt,sha256=Yte_6mb-bh-I_lQwMjk1GijZkxPoX4Zmp3kBftC1ZlA,9
40
- semantio-0.0.1.dist-info/RECORD,,
38
+ semantio-0.0.2.dist-info/LICENSE,sha256=teQbWD2Zlcl1_Fo29o2tNbs6G26hbCQiUzds5fQGYlY,1063
39
+ semantio-0.0.2.dist-info/METADATA,sha256=LsBDOM1gPw6rbzZ_-TIf4XJxf3DEWZ7s8KDd8_cDMw0,6800
40
+ semantio-0.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
41
+ semantio-0.0.2.dist-info/entry_points.txt,sha256=zbPgevSLwcLpdRHqI_atE8EOt8lK2vRF1AoDflDTo18,53
42
+ semantio-0.0.2.dist-info/top_level.txt,sha256=Yte_6mb-bh-I_lQwMjk1GijZkxPoX4Zmp3kBftC1ZlA,9
43
+ semantio-0.0.2.dist-info/RECORD,,