semantio 0.0.1__tar.gz → 0.0.2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. {semantio-0.0.1 → semantio-0.0.2}/PKG-INFO +5 -3
  2. {semantio-0.0.1 → semantio-0.0.2}/README.md +3 -1
  3. {semantio-0.0.1 → semantio-0.0.2}/semantio/agent.py +119 -50
  4. {semantio-0.0.1 → semantio-0.0.2}/semantio/llm/__init__.py +12 -5
  5. semantio-0.0.2/semantio/llm/anthropic.py +52 -0
  6. semantio-0.0.2/semantio/llm/base_llm.py +21 -0
  7. semantio-0.0.2/semantio/llm/deepseek.py +27 -0
  8. semantio-0.0.2/semantio/llm/gemini.py +50 -0
  9. semantio-0.0.2/semantio/llm/groq.py +121 -0
  10. semantio-0.0.1/semantio/llm/openai.py → semantio-0.0.2/semantio/llm/mistral.py +9 -8
  11. semantio-0.0.2/semantio/llm/openai.py +136 -0
  12. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/PKG-INFO +5 -3
  13. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/SOURCES.txt +3 -2
  14. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/requires.txt +2 -3
  15. {semantio-0.0.1 → semantio-0.0.2}/setup.py +4 -5
  16. semantio-0.0.1/semantio/llm/anthropic.py +0 -39
  17. semantio-0.0.1/semantio/llm/base_llm.py +0 -12
  18. semantio-0.0.1/semantio/llm/groq.py +0 -39
  19. semantio-0.0.1/semantio/llm/llama.py +0 -0
  20. semantio-0.0.1/semantio/tools/web_browser.py +0 -153
  21. {semantio-0.0.1 → semantio-0.0.2}/LICENSE +0 -0
  22. {semantio-0.0.1 → semantio-0.0.2}/semantio/__init__.py +0 -0
  23. {semantio-0.0.1 → semantio-0.0.2}/semantio/api/__init__.py +0 -0
  24. {semantio-0.0.1 → semantio-0.0.2}/semantio/api/api_generator.py +0 -0
  25. {semantio-0.0.1 → semantio-0.0.2}/semantio/api/fastapi_app.py +0 -0
  26. {semantio-0.0.1 → semantio-0.0.2}/semantio/cli/__init__.py +0 -0
  27. {semantio-0.0.1 → semantio-0.0.2}/semantio/cli/main.py +0 -0
  28. {semantio-0.0.1 → semantio-0.0.2}/semantio/knowledge_base/__init__.py +0 -0
  29. {semantio-0.0.1 → semantio-0.0.2}/semantio/knowledge_base/document_loader.py +0 -0
  30. {semantio-0.0.1 → semantio-0.0.2}/semantio/knowledge_base/retriever.py +0 -0
  31. {semantio-0.0.1 → semantio-0.0.2}/semantio/knowledge_base/vector_store.py +0 -0
  32. {semantio-0.0.1 → semantio-0.0.2}/semantio/memory.py +0 -0
  33. {semantio-0.0.1 → semantio-0.0.2}/semantio/rag.py +0 -0
  34. {semantio-0.0.1 → semantio-0.0.2}/semantio/storage/__init__.py +0 -0
  35. {semantio-0.0.1 → semantio-0.0.2}/semantio/storage/cloud_storage.py +0 -0
  36. {semantio-0.0.1 → semantio-0.0.2}/semantio/storage/local_storage.py +0 -0
  37. {semantio-0.0.1 → semantio-0.0.2}/semantio/tools/__init__.py +0 -0
  38. {semantio-0.0.1 → semantio-0.0.2}/semantio/tools/base_tool.py +0 -0
  39. {semantio-0.0.1 → semantio-0.0.2}/semantio/tools/crypto.py +0 -0
  40. {semantio-0.0.1 → semantio-0.0.2}/semantio/tools/duckduckgo.py +0 -0
  41. {semantio-0.0.1 → semantio-0.0.2}/semantio/tools/stocks.py +0 -0
  42. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/__init__.py +0 -0
  43. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/config.py +0 -0
  44. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/date_utils.py +0 -0
  45. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/file_utils.py +0 -0
  46. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/logger.py +0 -0
  47. {semantio-0.0.1 → semantio-0.0.2}/semantio/utils/validation_utils.py +0 -0
  48. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/dependency_links.txt +0 -0
  49. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/entry_points.txt +0 -0
  50. {semantio-0.0.1 → semantio-0.0.2}/semantio.egg-info/top_level.txt +0 -0
  51. {semantio-0.0.1 → semantio-0.0.2}/setup.cfg +0 -0
@@ -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
@@ -94,7 +94,9 @@ Semantio/
94
94
  │ │ ├── __init__.py
95
95
  │ │ ├── openai.py # OpenAI integration
96
96
  │ │ ├── anthropic.py # Anthropic (Claude) integration
97
- │ │ ├── llama.py # Llama 2 integration
97
+ │ │ ├── deepseek.py # Deepseek integration
98
+ │ │ ├── gemini.py # Gemini integration
99
+ │ │ ├── mistral.py # Mistral integration
98
100
  │ │ └── base_llm.py # Base class for LLMs
99
101
  │ ├── knowledge_base/ # Knowledge base integration
100
102
  │ │ ├── __init__.py
@@ -75,7 +75,9 @@ Semantio/
75
75
  │ │ ├── __init__.py
76
76
  │ │ ├── openai.py # OpenAI integration
77
77
  │ │ ├── anthropic.py # Anthropic (Claude) integration
78
- │ │ ├── llama.py # Llama 2 integration
78
+ │ │ ├── deepseek.py # Deepseek integration
79
+ │ │ ├── gemini.py # Gemini integration
80
+ │ │ ├── mistral.py # Mistral integration
79
81
  │ │ └── base_llm.py # Base class for LLMs
80
82
  │ ├── knowledge_base/ # Knowledge base integration
81
83
  │ │ ├── __init__.py
@@ -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"
@@ -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}")
@@ -0,0 +1,52 @@
1
+ import os
2
+ from typing import List, Dict, Optional
3
+ from .base_llm import BaseLLM
4
+ import anthropic
5
+
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
+ """
15
+ self.model = model
16
+ self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
17
+ if not self.api_key:
18
+ raise ValueError("Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or pass it explicitly.")
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.
29
+
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})
41
+
42
+ # Call the Anthropic API
43
+ response = self.client.messages.create(
44
+ model=self.model,
45
+ max_tokens=1024,
46
+ messages=messages,
47
+ )
48
+
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}")
@@ -0,0 +1,21 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List, Dict, Optional
3
+
4
+ class BaseLLM(ABC):
5
+ @abstractmethod
6
+ def generate(
7
+ self,
8
+ prompt: str,
9
+ context: Optional[List[Dict]] = None,
10
+ memory: Optional[List[Dict]] = None,
11
+ ) -> str:
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
@@ -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}")
@@ -0,0 +1,121 @@
1
+ import base64
2
+ from typing import Optional, List, Dict
3
+ from .base_llm import BaseLLM
4
+ import groq
5
+ import os
6
+
7
+ class GroqLlm(BaseLLM):
8
+ def __init__(
9
+ self,
10
+ model: str = "mixtral-8x7b-32768", # Default Groq model
11
+ api_key: Optional[str] = None,
12
+ ):
13
+ self.model = model
14
+ self.api_key = api_key or os.getenv("GROQ_API_KEY")
15
+ if not self.api_key:
16
+ raise ValueError("Groq API key is required. Set GROQ_API_KEY environment variable or pass it explicitly.")
17
+ self.client = groq.Client(api_key=self.api_key)
18
+
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
+ """
35
+ # Prepare messages for the Groq 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 Groq API
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
+
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}")
@@ -1,15 +1,15 @@
1
1
  from typing import List, Dict, Optional
2
2
  from .base_llm import BaseLLM
3
- import openai
3
+ from mistralai import Mistral
4
4
  import os
5
5
 
6
- class OpenAILlm(BaseLLM):
7
- def __init__(self, model: str = "gpt-4", api_key: Optional[str] = None):
6
+ class MistralLLM(BaseLLM):
7
+ def __init__(self, model: str = "mistral-large-latest", api_key: Optional[str] = None):
8
8
  self.model = model
9
- self.api_key = api_key or os.getenv("OPENAI_API_KEY")
9
+ self.api_key = api_key or os.getenv("MISTRAL_API_KEY")
10
10
  if not self.api_key:
11
- raise ValueError("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it explicitly.")
12
- openai.api_key = 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
13
 
14
14
  def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
15
15
  messages = []
@@ -19,8 +19,9 @@ class OpenAILlm(BaseLLM):
19
19
  messages.append({"role": "system", "content": "Context: " + str(context)})
20
20
  messages.append({"role": "user", "content": prompt})
21
21
 
22
- response = openai.ChatCompletion.create(
22
+ response = self.client.chat.complete(
23
23
  model=self.model,
24
24
  messages=messages,
25
25
  )
26
- return response.choices[0].message["content"]
26
+
27
+ return response.choices[0].message.content
@@ -0,0 +1,136 @@
1
+ from typing import List, Dict, Optional
2
+ from .base_llm import BaseLLM
3
+ from openai import OpenAI
4
+ import os
5
+ import base64
6
+
7
+ class OpenAILlm(BaseLLM):
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
+ """
16
+ self.model = model
17
+ self.api_key = api_key or os.getenv("OPENAI_API_KEY")
18
+ if not self.api_key:
19
+ raise ValueError("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it explicitly.")
20
+ self.client = OpenAI(api_key=self.api_key)
21
+
22
+ def generate(self, prompt: str, context: Optional[List[Dict]] = None, memory: Optional[List[Dict]] = None) -> str:
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
@@ -94,7 +94,9 @@ Semantio/
94
94
  │ │ ├── __init__.py
95
95
  │ │ ├── openai.py # OpenAI integration
96
96
  │ │ ├── anthropic.py # Anthropic (Claude) integration
97
- │ │ ├── llama.py # Llama 2 integration
97
+ │ │ ├── deepseek.py # Deepseek integration
98
+ │ │ ├── gemini.py # Gemini integration
99
+ │ │ ├── mistral.py # Mistral integration
98
100
  │ │ └── base_llm.py # Base class for LLMs
99
101
  │ ├── knowledge_base/ # Knowledge base integration
100
102
  │ │ ├── __init__.py
@@ -23,8 +23,10 @@ semantio/knowledge_base/vector_store.py
23
23
  semantio/llm/__init__.py
24
24
  semantio/llm/anthropic.py
25
25
  semantio/llm/base_llm.py
26
+ semantio/llm/deepseek.py
27
+ semantio/llm/gemini.py
26
28
  semantio/llm/groq.py
27
- semantio/llm/llama.py
29
+ semantio/llm/mistral.py
28
30
  semantio/llm/openai.py
29
31
  semantio/storage/__init__.py
30
32
  semantio/storage/cloud_storage.py
@@ -34,7 +36,6 @@ semantio/tools/base_tool.py
34
36
  semantio/tools/crypto.py
35
37
  semantio/tools/duckduckgo.py
36
38
  semantio/tools/stocks.py
37
- semantio/tools/web_browser.py
38
39
  semantio/utils/__init__.py
39
40
  semantio/utils/config.py
40
41
  semantio/utils/date_utils.py
@@ -1,7 +1,8 @@
1
1
  openai
2
2
  anthropic
3
3
  groq
4
- langchain
4
+ google-genai
5
+ mistralai
5
6
  faiss-cpu
6
7
  pydantic
7
8
  requests
@@ -14,5 +15,3 @@ sentence-transformers
14
15
  fuzzywuzzy
15
16
  duckduckgo-search
16
17
  yfinance
17
- forex-python
18
- qrcode
@@ -2,8 +2,8 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="semantio",
5
- version="0.0.1",
6
- description="A powerful SDK for building AI agents with RAG capabilities.",
5
+ version="0.0.2",
6
+ description="A powerful SDK for building AI agents",
7
7
  long_description=open("README.md").read(),
8
8
  long_description_content_type="text/markdown",
9
9
  author="Rakesh",
@@ -14,7 +14,8 @@ setup(
14
14
  "openai",
15
15
  "anthropic",
16
16
  "groq",
17
- "langchain",
17
+ "google-genai",
18
+ "mistralai",
18
19
  "faiss-cpu", # For vector storage
19
20
  "pydantic", # For data validation
20
21
  "requests", # For web tools
@@ -27,8 +28,6 @@ setup(
27
28
  "fuzzywuzzy", # For fuzzy string matching
28
29
  "duckduckgo-search", # For DuckDuckGo search
29
30
  "yfinance", # For stock/crypto prices
30
- "forex-python", # For currency conversion
31
- "qrcode", # For QR code generation
32
31
 
33
32
  ],
34
33
  classifiers=[
@@ -1,39 +0,0 @@
1
- from typing import List, Dict, Optional
2
- from .base_llm import BaseLLM
3
- import anthropic
4
- import os
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
- ):
12
- self.model = model
13
- self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
14
- if not self.api_key:
15
- 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)
17
-
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})
31
-
32
- # Call Anthropic API
33
- response = self.client.completion(
34
- model=self.model,
35
- messages=messages,
36
- )
37
-
38
- # Extract and return the response
39
- return response.choices[0].message.content
@@ -1,12 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import List, Dict, Optional
3
-
4
- class BaseLLM(ABC):
5
- @abstractmethod
6
- def generate(
7
- self,
8
- prompt: str,
9
- context: Optional[List[Dict]] = None,
10
- memory: Optional[List[Dict]] = None,
11
- ) -> str:
12
- pass
@@ -1,39 +0,0 @@
1
- from typing import List, Dict, Optional
2
- from .base_llm import BaseLLM
3
- import groq
4
- import os
5
-
6
- class GroqLlm(BaseLLM):
7
- def __init__(
8
- self,
9
- model: str = "mixtral-8x7b-32768", # Default Groq model
10
- api_key: Optional[str] = None,
11
- ):
12
- self.model = model
13
- self.api_key = api_key or os.getenv("GROQ_API_KEY")
14
- if not self.api_key:
15
- raise ValueError("Groq API key is required. Set GROQ_API_KEY environment variable or pass it explicitly.")
16
- self.client = groq.Client(api_key=self.api_key)
17
-
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 Groq 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})
31
-
32
- # Call Groq API
33
- response = self.client.chat.completions.create(
34
- model=self.model,
35
- messages=messages,
36
- )
37
-
38
- # Extract and return the response
39
- return response.choices[0].message.content
File without changes
@@ -1,153 +0,0 @@
1
- from typing import Dict, Any, Optional, List
2
- from playwright.async_api import async_playwright
3
- import asyncio
4
- import logging
5
-
6
- logger = logging.getLogger(__name__)
7
-
8
- class WebBrowserTool:
9
- """
10
- A tool for performing browser automation tasks using Playwright.
11
- """
12
-
13
- def __init__(self, headless: bool = True):
14
- """
15
- Initialize the WebBrowserTool.
16
-
17
- Args:
18
- headless (bool): Whether to run the browser in headless mode (default: True).
19
- """
20
- self.headless = headless
21
- self.browser = None
22
- self.context = None
23
- self.page = None
24
-
25
- async def start(self):
26
- """
27
- Start the browser and create a new context and page.
28
- """
29
- self.playwright = await async_playwright().start()
30
- self.browser = await self.playwright.chromium.launch(headless=self.headless)
31
- self.context = await self.browser.new_context()
32
- self.page = await self.context.new_page()
33
- logger.info("Browser started successfully.")
34
-
35
- async def close(self):
36
- """
37
- Close the browser and cleanup resources.
38
- """
39
- if self.browser:
40
- await self.browser.close()
41
- await self.playwright.stop()
42
- logger.info("Browser closed successfully.")
43
-
44
- async def navigate(self, url: str) -> str:
45
- """
46
- Navigate to a specific URL.
47
-
48
- Args:
49
- url (str): The URL to navigate to.
50
-
51
- Returns:
52
- str: The page title after navigation.
53
- """
54
- if not self.page:
55
- raise RuntimeError("Browser is not started. Call start() first.")
56
-
57
- await self.page.goto(url)
58
- title = await self.page.title()
59
- logger.info(f"Navigated to {url}. Page title: {title}")
60
- return title
61
-
62
- async def fill_form(self, fields: Dict[str, str]) -> str:
63
- """
64
- Fill a form with the provided fields.
65
-
66
- Args:
67
- fields (Dict[str, str]): A dictionary of field names and values to fill.
68
-
69
- Returns:
70
- str: A success message.
71
- """
72
- if not self.page:
73
- raise RuntimeError("Browser is not started. Call start() first.")
74
-
75
- for field, value in fields.items():
76
- await self.page.fill(f'input[name="{field}"]', value)
77
- logger.info(f"Filled field '{field}' with value '{value}'.")
78
-
79
- return "Form filled successfully."
80
-
81
- async def click(self, selector: str) -> str:
82
- """
83
- Click an element on the page.
84
-
85
- Args:
86
- selector (str): The CSS selector of the element to click.
87
-
88
- Returns:
89
- str: A success message.
90
- """
91
- if not self.page:
92
- raise RuntimeError("Browser is not started. Call start() first.")
93
-
94
- await self.page.click(selector)
95
- logger.info(f"Clicked element with selector '{selector}'.")
96
- return f"Clicked element: {selector}"
97
-
98
- async def scrape(self, selector: str) -> List[Dict[str, str]]:
99
- """
100
- Scrape data from the page.
101
-
102
- Args:
103
- selector (str): The CSS selector of the elements to scrape.
104
-
105
- Returns:
106
- List[Dict[str, str]]: A list of dictionaries containing the scraped data.
107
- """
108
- if not self.page:
109
- raise RuntimeError("Browser is not started. Call start() first.")
110
-
111
- elements = await self.page.query_selector_all(selector)
112
- scraped_data = []
113
- for element in elements:
114
- text = await element.inner_text()
115
- scraped_data.append({"text": text.strip()})
116
- logger.info(f"Scraped text: {text.strip()}")
117
-
118
- return scraped_data
119
-
120
- async def execute_step(self, step: Dict[str, Any]) -> str:
121
- """
122
- Execute a browser automation step.
123
-
124
- Args:
125
- step (Dict[str, Any]): A dictionary containing the step details.
126
- - "action": The action to perform (e.g., "navigate", "fill_form", "click", "scrape").
127
- - "details": The details required for the action (e.g., URL, form fields, selector).
128
- - "website": The website to perform the action on (optional).
129
-
130
- Returns:
131
- str: The result of the step execution.
132
- """
133
- action = step.get("action")
134
- details = step.get("details")
135
- website = step.get("website", "https://www.google.com")
136
-
137
- if not self.page:
138
- await self.start()
139
-
140
- try:
141
- if action == "navigate":
142
- return await self.navigate(details)
143
- elif action == "fill_form":
144
- return await self.fill_form(details)
145
- elif action == "click":
146
- return await self.click(details)
147
- elif action == "scrape":
148
- return str(await self.scrape(details))
149
- else:
150
- return f"Unknown action: {action}"
151
- except Exception as e:
152
- logger.error(f"Error executing step: {e}")
153
- return f"Error executing step: {e}"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes