agnt5 0.1.0__cp39-abi3-macosx_11_0_arm64.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.
- agnt5/__init__.py +307 -0
- agnt5/__pycache__/__init__.cpython-311.pyc +0 -0
- agnt5/__pycache__/agent.cpython-311.pyc +0 -0
- agnt5/__pycache__/context.cpython-311.pyc +0 -0
- agnt5/__pycache__/durable.cpython-311.pyc +0 -0
- agnt5/__pycache__/extraction.cpython-311.pyc +0 -0
- agnt5/__pycache__/memory.cpython-311.pyc +0 -0
- agnt5/__pycache__/reflection.cpython-311.pyc +0 -0
- agnt5/__pycache__/runtime.cpython-311.pyc +0 -0
- agnt5/__pycache__/task.cpython-311.pyc +0 -0
- agnt5/__pycache__/tool.cpython-311.pyc +0 -0
- agnt5/__pycache__/tracing.cpython-311.pyc +0 -0
- agnt5/__pycache__/types.cpython-311.pyc +0 -0
- agnt5/__pycache__/workflow.cpython-311.pyc +0 -0
- agnt5/_core.abi3.so +0 -0
- agnt5/agent.py +1086 -0
- agnt5/context.py +406 -0
- agnt5/durable.py +1050 -0
- agnt5/extraction.py +410 -0
- agnt5/llm/__init__.py +179 -0
- agnt5/llm/__pycache__/__init__.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/anthropic.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/azure.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/base.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/google.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/mistral.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/openai.cpython-311.pyc +0 -0
- agnt5/llm/__pycache__/together.cpython-311.pyc +0 -0
- agnt5/llm/anthropic.py +319 -0
- agnt5/llm/azure.py +348 -0
- agnt5/llm/base.py +315 -0
- agnt5/llm/google.py +373 -0
- agnt5/llm/mistral.py +330 -0
- agnt5/llm/model_registry.py +467 -0
- agnt5/llm/models.json +227 -0
- agnt5/llm/openai.py +334 -0
- agnt5/llm/together.py +377 -0
- agnt5/memory.py +746 -0
- agnt5/reflection.py +514 -0
- agnt5/runtime.py +699 -0
- agnt5/task.py +476 -0
- agnt5/testing.py +451 -0
- agnt5/tool.py +516 -0
- agnt5/tracing.py +624 -0
- agnt5/types.py +210 -0
- agnt5/workflow.py +897 -0
- agnt5-0.1.0.dist-info/METADATA +93 -0
- agnt5-0.1.0.dist-info/RECORD +49 -0
- agnt5-0.1.0.dist-info/WHEEL +4 -0
agnt5/extraction.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data extraction utilities for AGNT5 SDK.
|
|
3
|
+
|
|
4
|
+
Provides specialized tools for extracting structured data from text,
|
|
5
|
+
particularly JSON data extraction with validation and error handling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from enum import Enum
|
|
13
|
+
|
|
14
|
+
from .task import task, json_extraction_task, OutputFormat, TaskResult
|
|
15
|
+
from .types import Message, MessageRole
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExtractionStrategy(Enum):
|
|
19
|
+
"""Different strategies for data extraction."""
|
|
20
|
+
REGEX = "regex"
|
|
21
|
+
LLM_GUIDED = "llm_guided"
|
|
22
|
+
TEMPLATE_BASED = "template_based"
|
|
23
|
+
HYBRID = "hybrid"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ExtractionPattern:
|
|
28
|
+
"""Pattern for data extraction."""
|
|
29
|
+
name: str
|
|
30
|
+
pattern: str
|
|
31
|
+
description: Optional[str] = None
|
|
32
|
+
required: bool = True
|
|
33
|
+
data_type: Type = str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class JSONExtractor:
|
|
37
|
+
"""Utility class for JSON data extraction."""
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
self.common_patterns = {
|
|
41
|
+
"json_block": r'```json\s*(\{.*?\})\s*```',
|
|
42
|
+
"json_object": r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}',
|
|
43
|
+
"json_array": r'\[[^\[\]]*(?:\[[^\[\]]*\][^\[\]]*)*\]',
|
|
44
|
+
"code_block": r'```(?:json)?\s*(\{.*?\}|\[.*?\])\s*```',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def extract_json_from_text(self, text: str, strategy: ExtractionStrategy = ExtractionStrategy.HYBRID) -> List[Dict[str, Any]]:
|
|
48
|
+
"""
|
|
49
|
+
Extract JSON objects from text using various strategies.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
text: Input text to extract JSON from
|
|
53
|
+
strategy: Extraction strategy to use
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List of extracted JSON objects
|
|
57
|
+
"""
|
|
58
|
+
if strategy == ExtractionStrategy.REGEX:
|
|
59
|
+
return self._extract_with_regex(text)
|
|
60
|
+
elif strategy == ExtractionStrategy.TEMPLATE_BASED:
|
|
61
|
+
return self._extract_with_templates(text)
|
|
62
|
+
elif strategy == ExtractionStrategy.HYBRID:
|
|
63
|
+
return self._extract_hybrid(text)
|
|
64
|
+
else:
|
|
65
|
+
return self._extract_with_regex(text) # Default fallback
|
|
66
|
+
|
|
67
|
+
def _extract_with_regex(self, text: str) -> List[Dict[str, Any]]:
|
|
68
|
+
"""Extract JSON using regex patterns."""
|
|
69
|
+
results = []
|
|
70
|
+
|
|
71
|
+
# Try different patterns in order of specificity
|
|
72
|
+
patterns = [
|
|
73
|
+
self.common_patterns["json_block"],
|
|
74
|
+
self.common_patterns["code_block"],
|
|
75
|
+
self.common_patterns["json_object"],
|
|
76
|
+
self.common_patterns["json_array"],
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
for pattern in patterns:
|
|
80
|
+
matches = re.finditer(pattern, text, re.DOTALL | re.IGNORECASE)
|
|
81
|
+
for match in matches:
|
|
82
|
+
json_text = match.group(1) if match.groups() else match.group(0)
|
|
83
|
+
try:
|
|
84
|
+
parsed = json.loads(json_text)
|
|
85
|
+
if isinstance(parsed, dict):
|
|
86
|
+
results.append(parsed)
|
|
87
|
+
elif isinstance(parsed, list):
|
|
88
|
+
# If it's a list of objects, add each one
|
|
89
|
+
for item in parsed:
|
|
90
|
+
if isinstance(item, dict):
|
|
91
|
+
results.append(item)
|
|
92
|
+
except json.JSONDecodeError:
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
return results
|
|
96
|
+
|
|
97
|
+
def _extract_with_templates(self, text: str) -> List[Dict[str, Any]]:
|
|
98
|
+
"""Extract JSON using common templates and structures."""
|
|
99
|
+
results = []
|
|
100
|
+
|
|
101
|
+
# Look for common JSON-like structures
|
|
102
|
+
# Pattern: key: value pairs
|
|
103
|
+
key_value_pattern = r'(\w+):\s*(["\'].*?["\']|\d+(?:\.\d+)?|true|false|null)'
|
|
104
|
+
matches = re.findall(key_value_pattern, text)
|
|
105
|
+
|
|
106
|
+
if matches:
|
|
107
|
+
# Build a JSON object from key-value pairs
|
|
108
|
+
obj = {}
|
|
109
|
+
for key, value in matches:
|
|
110
|
+
try:
|
|
111
|
+
# Try to parse the value as JSON
|
|
112
|
+
parsed_value = json.loads(value)
|
|
113
|
+
obj[key] = parsed_value
|
|
114
|
+
except json.JSONDecodeError:
|
|
115
|
+
# Keep as string if not valid JSON
|
|
116
|
+
obj[key] = value.strip('"\'')
|
|
117
|
+
|
|
118
|
+
if obj:
|
|
119
|
+
results.append(obj)
|
|
120
|
+
|
|
121
|
+
return results
|
|
122
|
+
|
|
123
|
+
def _extract_hybrid(self, text: str) -> List[Dict[str, Any]]:
|
|
124
|
+
"""Combine multiple extraction strategies."""
|
|
125
|
+
results = []
|
|
126
|
+
|
|
127
|
+
# First try regex-based extraction
|
|
128
|
+
regex_results = self._extract_with_regex(text)
|
|
129
|
+
results.extend(regex_results)
|
|
130
|
+
|
|
131
|
+
# If no results, try template-based
|
|
132
|
+
if not results:
|
|
133
|
+
template_results = self._extract_with_templates(text)
|
|
134
|
+
results.extend(template_results)
|
|
135
|
+
|
|
136
|
+
# Remove duplicates while preserving order
|
|
137
|
+
seen = set()
|
|
138
|
+
unique_results = []
|
|
139
|
+
for result in results:
|
|
140
|
+
result_str = json.dumps(result, sort_keys=True)
|
|
141
|
+
if result_str not in seen:
|
|
142
|
+
seen.add(result_str)
|
|
143
|
+
unique_results.append(result)
|
|
144
|
+
|
|
145
|
+
return unique_results
|
|
146
|
+
|
|
147
|
+
def clean_and_validate_json(self, json_text: str) -> Optional[Dict[str, Any]]:
|
|
148
|
+
"""Clean and validate JSON text."""
|
|
149
|
+
# Remove common formatting issues
|
|
150
|
+
cleaned = json_text.strip()
|
|
151
|
+
|
|
152
|
+
# Remove markdown code block markers
|
|
153
|
+
cleaned = re.sub(r'^```(?:json)?\s*', '', cleaned, flags=re.MULTILINE)
|
|
154
|
+
cleaned = re.sub(r'\s*```$', '', cleaned, flags=re.MULTILINE)
|
|
155
|
+
|
|
156
|
+
# Fix common JSON issues
|
|
157
|
+
cleaned = self._fix_common_json_issues(cleaned)
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
return json.loads(cleaned)
|
|
161
|
+
except json.JSONDecodeError as e:
|
|
162
|
+
print(f"JSON validation failed: {e}")
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
def _fix_common_json_issues(self, text: str) -> str:
|
|
166
|
+
"""Fix common JSON formatting issues."""
|
|
167
|
+
# Fix single quotes to double quotes
|
|
168
|
+
text = re.sub(r"'([^']*)':", r'"\1":', text)
|
|
169
|
+
text = re.sub(r":\s*'([^']*)'", r': "\1"', text)
|
|
170
|
+
|
|
171
|
+
# Fix trailing commas
|
|
172
|
+
text = re.sub(r',\s*}', '}', text)
|
|
173
|
+
text = re.sub(r',\s*]', ']', text)
|
|
174
|
+
|
|
175
|
+
# Fix missing commas between object properties
|
|
176
|
+
text = re.sub(r'"\s*\n\s*"', '",\n"', text)
|
|
177
|
+
|
|
178
|
+
# Fix unquoted keys
|
|
179
|
+
text = re.sub(r'(\w+):', r'"\1":', text)
|
|
180
|
+
|
|
181
|
+
return text
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Pre-built extraction tasks
|
|
185
|
+
@json_extraction_task(
|
|
186
|
+
name="extract_json_from_text",
|
|
187
|
+
description="Extract JSON objects from any text content"
|
|
188
|
+
)
|
|
189
|
+
async def extract_json_from_text(text: str, strategy: str = "hybrid") -> List[Dict[str, Any]]:
|
|
190
|
+
"""Extract JSON objects from text."""
|
|
191
|
+
extractor = JSONExtractor()
|
|
192
|
+
strategy_enum = ExtractionStrategy(strategy) if strategy in [s.value for s in ExtractionStrategy] else ExtractionStrategy.HYBRID
|
|
193
|
+
return extractor.extract_json_from_text(text, strategy_enum)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@json_extraction_task(
|
|
197
|
+
name="extract_structured_data",
|
|
198
|
+
description="Extract structured data using LLM guidance",
|
|
199
|
+
schema={
|
|
200
|
+
"type": "object",
|
|
201
|
+
"properties": {
|
|
202
|
+
"extracted_data": {"type": "array"},
|
|
203
|
+
"confidence": {"type": "number"},
|
|
204
|
+
"method": {"type": "string"}
|
|
205
|
+
},
|
|
206
|
+
"required": ["extracted_data"]
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
async def extract_structured_data(
|
|
210
|
+
text: str,
|
|
211
|
+
data_description: str,
|
|
212
|
+
agent: Optional['Agent'] = None
|
|
213
|
+
) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Extract structured data using LLM guidance.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
text: Input text to extract data from
|
|
219
|
+
data_description: Description of what data to extract
|
|
220
|
+
agent: Optional agent to use for extraction (uses default if not provided)
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Dictionary with extracted data, confidence score, and method used
|
|
224
|
+
"""
|
|
225
|
+
from .agent import Agent
|
|
226
|
+
|
|
227
|
+
# First try regex-based extraction
|
|
228
|
+
extractor = JSONExtractor()
|
|
229
|
+
regex_results = extractor.extract_json_from_text(text, ExtractionStrategy.REGEX)
|
|
230
|
+
|
|
231
|
+
if regex_results:
|
|
232
|
+
return {
|
|
233
|
+
"extracted_data": regex_results,
|
|
234
|
+
"confidence": 0.8,
|
|
235
|
+
"method": "regex"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# If no results, use LLM guidance
|
|
239
|
+
if agent is None:
|
|
240
|
+
agent = Agent(name="data_extractor", model="gpt-4o-mini")
|
|
241
|
+
|
|
242
|
+
prompt = f"""Extract structured data from the following text.
|
|
243
|
+
|
|
244
|
+
Data to extract: {data_description}
|
|
245
|
+
|
|
246
|
+
Text:
|
|
247
|
+
{text}
|
|
248
|
+
|
|
249
|
+
Please return the extracted data as a JSON object. If multiple items are found, return them as an array.
|
|
250
|
+
Focus on accuracy and completeness. If no relevant data is found, return an empty object.
|
|
251
|
+
|
|
252
|
+
Example format:
|
|
253
|
+
{{
|
|
254
|
+
"item1": {{"field1": "value1", "field2": "value2"}},
|
|
255
|
+
"item2": {{"field1": "value3", "field2": "value4"}}
|
|
256
|
+
}}
|
|
257
|
+
|
|
258
|
+
Extracted data:"""
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
response = await agent.run(prompt)
|
|
262
|
+
|
|
263
|
+
# Extract JSON from agent response
|
|
264
|
+
llm_results = extractor.extract_json_from_text(response.content, ExtractionStrategy.HYBRID)
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"extracted_data": llm_results if llm_results else [],
|
|
268
|
+
"confidence": 0.9 if llm_results else 0.1,
|
|
269
|
+
"method": "llm_guided"
|
|
270
|
+
}
|
|
271
|
+
except Exception as e:
|
|
272
|
+
return {
|
|
273
|
+
"extracted_data": [],
|
|
274
|
+
"confidence": 0.0,
|
|
275
|
+
"method": "failed",
|
|
276
|
+
"error": str(e)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@json_extraction_task(
|
|
281
|
+
name="clean_and_validate_json",
|
|
282
|
+
description="Clean and validate JSON text for proper formatting"
|
|
283
|
+
)
|
|
284
|
+
async def clean_and_validate_json(json_text: str) -> Dict[str, Any]:
|
|
285
|
+
"""Clean and validate JSON text."""
|
|
286
|
+
extractor = JSONExtractor()
|
|
287
|
+
result = extractor.clean_and_validate_json(json_text)
|
|
288
|
+
|
|
289
|
+
if result is None:
|
|
290
|
+
raise ValueError("Invalid JSON that cannot be cleaned")
|
|
291
|
+
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@task(
|
|
296
|
+
name="extract_entities",
|
|
297
|
+
description="Extract named entities and their relationships",
|
|
298
|
+
output_format=OutputFormat.JSON,
|
|
299
|
+
output_schema={
|
|
300
|
+
"type": "object",
|
|
301
|
+
"properties": {
|
|
302
|
+
"entities": {
|
|
303
|
+
"type": "array",
|
|
304
|
+
"items": {
|
|
305
|
+
"type": "object",
|
|
306
|
+
"properties": {
|
|
307
|
+
"text": {"type": "string"},
|
|
308
|
+
"type": {"type": "string"},
|
|
309
|
+
"confidence": {"type": "number"}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
"relationships": {
|
|
314
|
+
"type": "array",
|
|
315
|
+
"items": {
|
|
316
|
+
"type": "object",
|
|
317
|
+
"properties": {
|
|
318
|
+
"source": {"type": "string"},
|
|
319
|
+
"target": {"type": "string"},
|
|
320
|
+
"relation": {"type": "string"}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
async def extract_entities(text: str, entity_types: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
328
|
+
"""
|
|
329
|
+
Extract named entities from text.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
text: Input text
|
|
333
|
+
entity_types: Optional list of entity types to focus on
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Dictionary with entities and relationships
|
|
337
|
+
"""
|
|
338
|
+
# This is a simplified implementation
|
|
339
|
+
# In a real scenario, you'd use NLP libraries like spaCy or use LLM for extraction
|
|
340
|
+
|
|
341
|
+
entities = []
|
|
342
|
+
relationships = []
|
|
343
|
+
|
|
344
|
+
# Simple regex-based entity extraction (can be enhanced)
|
|
345
|
+
patterns = {
|
|
346
|
+
"PERSON": r'\b[A-Z][a-z]+ [A-Z][a-z]+\b',
|
|
347
|
+
"EMAIL": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
|
|
348
|
+
"PHONE": r'\b\d{3}-\d{3}-\d{4}\b|\b\(\d{3}\) \d{3}-\d{4}\b',
|
|
349
|
+
"DATE": r'\b\d{1,2}/\d{1,2}/\d{4}\b|\b\d{4}-\d{2}-\d{2}\b',
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
for entity_type, pattern in patterns.items():
|
|
353
|
+
if entity_types is None or entity_type in entity_types:
|
|
354
|
+
matches = re.finditer(pattern, text)
|
|
355
|
+
for match in matches:
|
|
356
|
+
entities.append({
|
|
357
|
+
"text": match.group(),
|
|
358
|
+
"type": entity_type,
|
|
359
|
+
"confidence": 0.8 # Simple confidence score
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
"entities": entities,
|
|
364
|
+
"relationships": relationships
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# Utility functions for common extraction patterns
|
|
369
|
+
def extract_urls(text: str) -> List[str]:
|
|
370
|
+
"""Extract URLs from text."""
|
|
371
|
+
url_pattern = r'https?://(?:[-\w.])+(?:[:\d]+)?(?:/(?:[\w/_.])*(?:\?(?:[\w&=%.])*)?(?:#(?:[\w.])*)?)?'
|
|
372
|
+
return re.findall(url_pattern, text)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def extract_emails(text: str) -> List[str]:
|
|
376
|
+
"""Extract email addresses from text."""
|
|
377
|
+
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
|
378
|
+
return re.findall(email_pattern, text)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def extract_phone_numbers(text: str) -> List[str]:
|
|
382
|
+
"""Extract phone numbers from text."""
|
|
383
|
+
phone_patterns = [
|
|
384
|
+
r'\b\d{3}-\d{3}-\d{4}\b',
|
|
385
|
+
r'\b\(\d{3}\) \d{3}-\d{4}\b',
|
|
386
|
+
r'\b\d{3}\.\d{3}\.\d{4}\b',
|
|
387
|
+
r'\b\d{10}\b'
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
numbers = []
|
|
391
|
+
for pattern in phone_patterns:
|
|
392
|
+
numbers.extend(re.findall(pattern, text))
|
|
393
|
+
|
|
394
|
+
return numbers
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def extract_dates(text: str) -> List[str]:
|
|
398
|
+
"""Extract dates from text."""
|
|
399
|
+
date_patterns = [
|
|
400
|
+
r'\b\d{1,2}/\d{1,2}/\d{4}\b',
|
|
401
|
+
r'\b\d{4}-\d{2}-\d{2}\b',
|
|
402
|
+
r'\b\d{1,2}-\d{1,2}-\d{4}\b',
|
|
403
|
+
r'\b[A-Za-z]+ \d{1,2}, \d{4}\b'
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
dates = []
|
|
407
|
+
for pattern in date_patterns:
|
|
408
|
+
dates.extend(re.findall(pattern, text))
|
|
409
|
+
|
|
410
|
+
return dates
|
agnt5/llm/__init__.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM provider integrations for AGNT5 SDK.
|
|
3
|
+
|
|
4
|
+
Provides unified interface for different LLM providers (Anthropic, OpenAI, etc.)
|
|
5
|
+
with consistent message handling, tool calling, and streaming support.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .base import (
|
|
9
|
+
LanguageModel,
|
|
10
|
+
LanguageModelResponse,
|
|
11
|
+
LanguageModelType,
|
|
12
|
+
Message,
|
|
13
|
+
Role,
|
|
14
|
+
TokenUsage,
|
|
15
|
+
ToolCall,
|
|
16
|
+
ToolResult,
|
|
17
|
+
LLMError,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Model registry is available but not exported by default
|
|
21
|
+
# Users can import directly if needed: from agnt5.llm.model_registry import ...
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from .anthropic import AnthropicLanguageModel
|
|
25
|
+
ANTHROPIC_AVAILABLE = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
ANTHROPIC_AVAILABLE = False
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from .openai import OpenAILanguageModel
|
|
31
|
+
OPENAI_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
OPENAI_AVAILABLE = False
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
from .google import GoogleLanguageModel
|
|
37
|
+
GOOGLE_AVAILABLE = True
|
|
38
|
+
except ImportError:
|
|
39
|
+
GOOGLE_AVAILABLE = False
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
from .mistral import MistralLanguageModel
|
|
43
|
+
MISTRAL_AVAILABLE = True
|
|
44
|
+
except ImportError:
|
|
45
|
+
MISTRAL_AVAILABLE = False
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
from .azure import AzureOpenAILanguageModel
|
|
49
|
+
AZURE_OPENAI_AVAILABLE = True
|
|
50
|
+
except ImportError:
|
|
51
|
+
AZURE_OPENAI_AVAILABLE = False
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
from .together import TogetherAILanguageModel
|
|
55
|
+
TOGETHER_AVAILABLE = True
|
|
56
|
+
except ImportError:
|
|
57
|
+
TOGETHER_AVAILABLE = False
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
# Base classes
|
|
61
|
+
"LanguageModel",
|
|
62
|
+
"LanguageModelResponse",
|
|
63
|
+
"LanguageModelType",
|
|
64
|
+
"Message",
|
|
65
|
+
"Role",
|
|
66
|
+
"TokenUsage",
|
|
67
|
+
"ToolCall",
|
|
68
|
+
"ToolResult",
|
|
69
|
+
"LLMError",
|
|
70
|
+
|
|
71
|
+
# Factory function
|
|
72
|
+
"create_llm",
|
|
73
|
+
|
|
74
|
+
# Provider availability flags
|
|
75
|
+
"ANTHROPIC_AVAILABLE",
|
|
76
|
+
"OPENAI_AVAILABLE",
|
|
77
|
+
"GOOGLE_AVAILABLE",
|
|
78
|
+
"MISTRAL_AVAILABLE",
|
|
79
|
+
"AZURE_OPENAI_AVAILABLE",
|
|
80
|
+
"TOGETHER_AVAILABLE",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
# Conditionally export providers
|
|
84
|
+
if ANTHROPIC_AVAILABLE:
|
|
85
|
+
__all__.append("AnthropicLanguageModel")
|
|
86
|
+
|
|
87
|
+
if OPENAI_AVAILABLE:
|
|
88
|
+
__all__.append("OpenAILanguageModel")
|
|
89
|
+
|
|
90
|
+
if GOOGLE_AVAILABLE:
|
|
91
|
+
__all__.append("GoogleLanguageModel")
|
|
92
|
+
|
|
93
|
+
if MISTRAL_AVAILABLE:
|
|
94
|
+
__all__.append("MistralLanguageModel")
|
|
95
|
+
|
|
96
|
+
if AZURE_OPENAI_AVAILABLE:
|
|
97
|
+
__all__.append("AzureOpenAILanguageModel")
|
|
98
|
+
|
|
99
|
+
if TOGETHER_AVAILABLE:
|
|
100
|
+
__all__.append("TogetherAILanguageModel")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_available_providers():
|
|
104
|
+
"""Get list of available LLM providers."""
|
|
105
|
+
providers = []
|
|
106
|
+
if ANTHROPIC_AVAILABLE:
|
|
107
|
+
providers.append("anthropic")
|
|
108
|
+
if OPENAI_AVAILABLE:
|
|
109
|
+
providers.append("openai")
|
|
110
|
+
if GOOGLE_AVAILABLE:
|
|
111
|
+
providers.append("google")
|
|
112
|
+
if MISTRAL_AVAILABLE:
|
|
113
|
+
providers.append("mistral")
|
|
114
|
+
if AZURE_OPENAI_AVAILABLE:
|
|
115
|
+
providers.append("azure_openai")
|
|
116
|
+
if TOGETHER_AVAILABLE:
|
|
117
|
+
providers.append("together")
|
|
118
|
+
return providers
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def create_llm(provider: str, model: str, **kwargs) -> LanguageModel:
|
|
122
|
+
"""
|
|
123
|
+
Factory function to create LLM instances.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
provider: Provider name ('anthropic', 'openai')
|
|
127
|
+
model: Model name
|
|
128
|
+
**kwargs: Provider-specific configuration
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
LanguageModel instance
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
ValueError: If provider is not available
|
|
135
|
+
"""
|
|
136
|
+
if provider == "anthropic":
|
|
137
|
+
if not ANTHROPIC_AVAILABLE:
|
|
138
|
+
raise ValueError("Anthropic provider not available. Install: pip install anthropic")
|
|
139
|
+
return AnthropicLanguageModel(
|
|
140
|
+
llm_model=LanguageModelType.from_string(model),
|
|
141
|
+
**kwargs
|
|
142
|
+
)
|
|
143
|
+
elif provider == "openai":
|
|
144
|
+
if not OPENAI_AVAILABLE:
|
|
145
|
+
raise ValueError("OpenAI provider not available. Install: pip install openai")
|
|
146
|
+
return OpenAILanguageModel(
|
|
147
|
+
llm_model=LanguageModelType.from_string(model),
|
|
148
|
+
**kwargs
|
|
149
|
+
)
|
|
150
|
+
elif provider == "google":
|
|
151
|
+
if not GOOGLE_AVAILABLE:
|
|
152
|
+
raise ValueError("Google provider not available. Install: pip install google-generativeai")
|
|
153
|
+
return GoogleLanguageModel(
|
|
154
|
+
llm_model=LanguageModelType.from_string(model),
|
|
155
|
+
**kwargs
|
|
156
|
+
)
|
|
157
|
+
elif provider == "mistral":
|
|
158
|
+
if not MISTRAL_AVAILABLE:
|
|
159
|
+
raise ValueError("Mistral provider not available. Install: pip install openai")
|
|
160
|
+
return MistralLanguageModel(
|
|
161
|
+
llm_model=LanguageModelType.from_string(model),
|
|
162
|
+
**kwargs
|
|
163
|
+
)
|
|
164
|
+
elif provider == "azure_openai":
|
|
165
|
+
if not AZURE_OPENAI_AVAILABLE:
|
|
166
|
+
raise ValueError("Azure OpenAI provider not available. Install: pip install openai")
|
|
167
|
+
return AzureOpenAILanguageModel(
|
|
168
|
+
llm_model=LanguageModelType.from_string(model),
|
|
169
|
+
**kwargs
|
|
170
|
+
)
|
|
171
|
+
elif provider == "together":
|
|
172
|
+
if not TOGETHER_AVAILABLE:
|
|
173
|
+
raise ValueError("Together AI provider not available. Install: pip install openai")
|
|
174
|
+
return TogetherAILanguageModel(
|
|
175
|
+
llm_model=LanguageModelType.from_string(model),
|
|
176
|
+
**kwargs
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
raise ValueError(f"Unknown provider: {provider}. Available: {get_available_providers()}")
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|