semantio 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,