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 +119 -50
- semantio/llm/__init__.py +12 -5
- semantio/llm/anthropic.py +41 -28
- semantio/llm/base_llm.py +10 -1
- semantio/llm/deepseek.py +27 -0
- semantio/llm/gemini.py +50 -0
- semantio/llm/groq.py +90 -8
- semantio/llm/mistral.py +27 -0
- semantio/llm/openai.py +125 -15
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/METADATA +7 -6
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/RECORD +15 -12
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/LICENSE +0 -0
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/WHEEL +0 -0
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/entry_points.txt +0 -0
- {semantio-0.0.1.dist-info → semantio-0.0.2.dist-info}/top_level.txt +0 -0
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
|
-
|
23
|
+
|
24
|
+
class Assistant(BaseModel):
|
24
25
|
# -*- Agent settings
|
25
|
-
name: Optional[str] = Field(None, description="Name of the
|
26
|
-
description: Optional[str] = Field(None, description="Description of the
|
27
|
-
instructions: Optional[List[str]] = Field(None, description="List of instructions for the
|
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
|
32
|
-
user_name: Optional[str] = Field("User", description="Name of the user interacting with the
|
33
|
-
emoji: Optional[str] = Field(":robot:", description="Emoji to represent the
|
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
|
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
|
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-
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
289
|
-
# Use the
|
290
|
-
|
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
|
-
#
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
7
|
-
def __init__(
|
8
|
-
|
9
|
-
|
10
|
-
|
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.
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
39
|
-
|
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.")
|
semantio/llm/deepseek.py
ADDED
@@ -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
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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}")
|
semantio/llm/mistral.py
ADDED
@@ -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
|
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-
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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.
|
4
|
-
Summary: A powerful SDK for building AI agents
|
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:
|
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
|
-
│ │ ├──
|
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=
|
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
|
15
|
-
semantio/llm/anthropic.py,sha256
|
16
|
-
semantio/llm/base_llm.py,sha256=
|
17
|
-
semantio/llm/
|
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/
|
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.
|
36
|
-
semantio-0.0.
|
37
|
-
semantio-0.0.
|
38
|
-
semantio-0.0.
|
39
|
-
semantio-0.0.
|
40
|
-
semantio-0.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|