isa-model 0.3.7__py3-none-any.whl → 0.3.9__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.
- isa_model/__init__.py +1 -1
- isa_model/client.py +41 -5
- isa_model/core/pricing_manager.py +248 -1
- isa_model/inference/services/llm/ollama_llm_service.py +43 -0
- isa_model/inference/services/llm/openai_llm_service.py +7 -5
- isa_model/inference/services/llm/yyds_llm_service.py +43 -1
- {isa_model-0.3.7.dist-info → isa_model-0.3.9.dist-info}/METADATA +1 -1
- {isa_model-0.3.7.dist-info → isa_model-0.3.9.dist-info}/RECORD +10 -20
- isa_model/inference/providers/__init__.py +0 -19
- isa_model/inference/providers/base_provider.py +0 -77
- isa_model/inference/providers/ml_provider.py +0 -50
- isa_model/inference/providers/modal_provider.py +0 -109
- isa_model/inference/providers/model_cache_manager.py +0 -341
- isa_model/inference/providers/ollama_provider.py +0 -92
- isa_model/inference/providers/openai_provider.py +0 -130
- isa_model/inference/providers/replicate_provider.py +0 -119
- isa_model/inference/providers/triton_provider.py +0 -439
- isa_model/inference/providers/yyds_provider.py +0 -108
- {isa_model-0.3.7.dist-info → isa_model-0.3.9.dist-info}/WHEEL +0 -0
- {isa_model-0.3.7.dist-info → isa_model-0.3.9.dist-info}/top_level.txt +0 -0
isa_model/__init__.py
CHANGED
isa_model/client.py
CHANGED
@@ -138,6 +138,7 @@ class ISAModelClient:
|
|
138
138
|
model_hint: Optional[str] = None,
|
139
139
|
provider_hint: Optional[str] = None,
|
140
140
|
stream: bool = False,
|
141
|
+
tools: Optional[List[Any]] = None,
|
141
142
|
**kwargs
|
142
143
|
) -> Union[Dict[str, Any], object]:
|
143
144
|
"""
|
@@ -150,6 +151,7 @@ class ISAModelClient:
|
|
150
151
|
model_hint: Optional model preference
|
151
152
|
provider_hint: Optional provider preference
|
152
153
|
stream: Enable streaming for text services (returns AsyncGenerator)
|
154
|
+
tools: Optional list of tools for function calling (only for text services)
|
153
155
|
**kwargs: Additional task-specific parameters
|
154
156
|
|
155
157
|
Returns:
|
@@ -174,6 +176,13 @@ class ISAModelClient:
|
|
174
176
|
async for token in await client.invoke("Hello", "chat", "text", stream=True):
|
175
177
|
print(token, end="", flush=True)
|
176
178
|
|
179
|
+
# Text with tools
|
180
|
+
await client.invoke("What's 5+3?", "chat", "text", tools=[calculator_function])
|
181
|
+
|
182
|
+
# Streaming with tools
|
183
|
+
async for token in await client.invoke("What's 5+3?", "chat", "text", stream=True, tools=[calculator_function]):
|
184
|
+
print(token, end="")
|
185
|
+
|
177
186
|
# Image generation
|
178
187
|
await client.invoke("A beautiful sunset", "generate_image", "image")
|
179
188
|
|
@@ -193,6 +202,7 @@ class ISAModelClient:
|
|
193
202
|
service_type=service_type,
|
194
203
|
model_hint=model_hint,
|
195
204
|
provider_hint=provider_hint,
|
205
|
+
tools=tools,
|
196
206
|
**kwargs
|
197
207
|
)
|
198
208
|
else:
|
@@ -202,6 +212,7 @@ class ISAModelClient:
|
|
202
212
|
service_type=service_type,
|
203
213
|
model_hint=model_hint,
|
204
214
|
provider_hint=provider_hint,
|
215
|
+
tools=tools,
|
205
216
|
**kwargs
|
206
217
|
)
|
207
218
|
|
@@ -213,6 +224,7 @@ class ISAModelClient:
|
|
213
224
|
service_type=service_type,
|
214
225
|
model_hint=model_hint,
|
215
226
|
provider_hint=provider_hint,
|
227
|
+
tools=tools,
|
216
228
|
**kwargs
|
217
229
|
)
|
218
230
|
else:
|
@@ -222,6 +234,7 @@ class ISAModelClient:
|
|
222
234
|
service_type=service_type,
|
223
235
|
model_hint=model_hint,
|
224
236
|
provider_hint=provider_hint,
|
237
|
+
tools=tools,
|
225
238
|
**kwargs
|
226
239
|
)
|
227
240
|
|
@@ -349,7 +362,8 @@ class ISAModelClient:
|
|
349
362
|
service_type: str,
|
350
363
|
model_name: str,
|
351
364
|
provider: str,
|
352
|
-
task: str
|
365
|
+
task: str,
|
366
|
+
tools: Optional[List[Any]] = None
|
353
367
|
) -> Any:
|
354
368
|
"""Get appropriate service instance"""
|
355
369
|
|
@@ -357,7 +371,11 @@ class ISAModelClient:
|
|
357
371
|
|
358
372
|
# Check cache first
|
359
373
|
if cache_key in self._service_cache:
|
360
|
-
|
374
|
+
service = self._service_cache[cache_key]
|
375
|
+
# If tools are needed, bind them to the service
|
376
|
+
if tools and service_type == "text":
|
377
|
+
return service.bind_tools(tools)
|
378
|
+
return service
|
361
379
|
|
362
380
|
try:
|
363
381
|
# Route to appropriate AIFactory method
|
@@ -387,6 +405,11 @@ class ISAModelClient:
|
|
387
405
|
|
388
406
|
# Cache the service
|
389
407
|
self._service_cache[cache_key] = service
|
408
|
+
|
409
|
+
# If tools are needed, bind them to the service
|
410
|
+
if tools and service_type == "text":
|
411
|
+
return service.bind_tools(tools)
|
412
|
+
|
390
413
|
return service
|
391
414
|
|
392
415
|
except Exception as e:
|
@@ -499,11 +522,20 @@ class ISAModelClient:
|
|
499
522
|
unified_task = task_mapping.get(task, task)
|
500
523
|
|
501
524
|
# Use unified invoke method
|
502
|
-
|
525
|
+
result = await service.invoke(
|
503
526
|
input_data=input_data,
|
504
527
|
task=unified_task,
|
505
528
|
**kwargs
|
506
529
|
)
|
530
|
+
|
531
|
+
# Handle the new response format from LLM services
|
532
|
+
# LLM services now return {"message": ..., "success": ..., "metadata": ...}
|
533
|
+
if isinstance(result, dict) and "message" in result:
|
534
|
+
# Extract the message object (AIMessage or string)
|
535
|
+
return result["message"]
|
536
|
+
|
537
|
+
# Fallback for other service types or legacy format
|
538
|
+
return result
|
507
539
|
|
508
540
|
async def _execute_image_task(self, service, input_data, task, **kwargs):
|
509
541
|
"""Execute image generation tasks using unified invoke method"""
|
@@ -616,6 +648,7 @@ class ISAModelClient:
|
|
616
648
|
service_type: str,
|
617
649
|
model_hint: Optional[str] = None,
|
618
650
|
provider_hint: Optional[str] = None,
|
651
|
+
tools: Optional[List[Any]] = None,
|
619
652
|
**kwargs
|
620
653
|
) -> Dict[str, Any]:
|
621
654
|
"""Local invoke using AI Factory (original logic)"""
|
@@ -634,7 +667,8 @@ class ISAModelClient:
|
|
634
667
|
service_type=service_type,
|
635
668
|
model_name=selected_model["model_id"],
|
636
669
|
provider=selected_model["provider"],
|
637
|
-
task=task
|
670
|
+
task=task,
|
671
|
+
tools=tools
|
638
672
|
)
|
639
673
|
|
640
674
|
# Step 3: Execute task with unified interface
|
@@ -823,6 +857,7 @@ class ISAModelClient:
|
|
823
857
|
service_type: str,
|
824
858
|
model_hint: Optional[str] = None,
|
825
859
|
provider_hint: Optional[str] = None,
|
860
|
+
tools: Optional[List[Any]] = None,
|
826
861
|
**kwargs
|
827
862
|
):
|
828
863
|
"""Local streaming using AI Factory"""
|
@@ -840,7 +875,8 @@ class ISAModelClient:
|
|
840
875
|
service_type=service_type,
|
841
876
|
model_name=selected_model["model_id"],
|
842
877
|
provider=selected_model["provider"],
|
843
|
-
task=task
|
878
|
+
task=task,
|
879
|
+
tools=tools
|
844
880
|
)
|
845
881
|
|
846
882
|
# Step 3: Yield tokens from the stream
|
@@ -15,6 +15,7 @@ from datetime import datetime, timedelta
|
|
15
15
|
from dataclasses import dataclass, field
|
16
16
|
|
17
17
|
from .types import Provider
|
18
|
+
from .config import config_manager
|
18
19
|
|
19
20
|
logger = logging.getLogger(__name__)
|
20
21
|
|
@@ -77,7 +78,17 @@ class PricingManager:
|
|
77
78
|
|
78
79
|
def _load_pricing_data(self):
|
79
80
|
"""Load pricing data from configuration files"""
|
80
|
-
# Try to load from
|
81
|
+
# Try to load from Supabase first
|
82
|
+
if self._load_from_supabase():
|
83
|
+
logger.info("Loaded pricing data from Supabase")
|
84
|
+
return
|
85
|
+
|
86
|
+
# Try to load from provider configurations
|
87
|
+
if self._load_from_provider_configs():
|
88
|
+
logger.info("Loaded pricing data from provider configurations")
|
89
|
+
return
|
90
|
+
|
91
|
+
# Try to load from specified config path
|
81
92
|
if self.config_path and self.config_path.exists():
|
82
93
|
self._load_from_file(self.config_path)
|
83
94
|
return
|
@@ -187,6 +198,242 @@ class PricingManager:
|
|
187
198
|
self._parse_pricing_data({"providers": default_pricing})
|
188
199
|
logger.info("Loaded default pricing data")
|
189
200
|
|
201
|
+
def _load_from_supabase(self) -> bool:
|
202
|
+
"""Try to load pricing data from Supabase models table"""
|
203
|
+
try:
|
204
|
+
global_config = config_manager.get_global_config()
|
205
|
+
if not global_config.use_supabase:
|
206
|
+
return False
|
207
|
+
|
208
|
+
# Import Supabase client
|
209
|
+
try:
|
210
|
+
from supabase import create_client, Client
|
211
|
+
except ImportError:
|
212
|
+
logger.debug("Supabase library not available")
|
213
|
+
return False
|
214
|
+
|
215
|
+
# Get Supabase credentials
|
216
|
+
supabase_url = global_config.supabase_url or os.getenv('SUPABASE_URL')
|
217
|
+
supabase_key = global_config.supabase_key or os.getenv('SUPABASE_ANON_KEY')
|
218
|
+
|
219
|
+
if not supabase_url or not supabase_key:
|
220
|
+
logger.debug("Supabase credentials not configured")
|
221
|
+
return False
|
222
|
+
|
223
|
+
# Create Supabase client
|
224
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
225
|
+
|
226
|
+
# Query models table for pricing information
|
227
|
+
result = supabase.table('models').select('model_id, provider, metadata').execute()
|
228
|
+
|
229
|
+
if not result.data:
|
230
|
+
logger.debug("No models found in Supabase")
|
231
|
+
return False
|
232
|
+
|
233
|
+
self.pricing_data = {}
|
234
|
+
loaded_count = 0
|
235
|
+
|
236
|
+
for model_record in result.data:
|
237
|
+
model_id = model_record.get('model_id')
|
238
|
+
provider = model_record.get('provider')
|
239
|
+
metadata = model_record.get('metadata', {})
|
240
|
+
|
241
|
+
if not model_id or not provider:
|
242
|
+
continue
|
243
|
+
|
244
|
+
# Extract pricing from metadata
|
245
|
+
pricing = self._extract_pricing_from_supabase_metadata(metadata, provider, model_id)
|
246
|
+
if pricing:
|
247
|
+
if provider not in self.pricing_data:
|
248
|
+
self.pricing_data[provider] = {}
|
249
|
+
self.pricing_data[provider][model_id] = pricing
|
250
|
+
loaded_count += 1
|
251
|
+
|
252
|
+
if loaded_count > 0:
|
253
|
+
logger.info(f"Loaded pricing for {loaded_count} models from Supabase")
|
254
|
+
return True
|
255
|
+
else:
|
256
|
+
logger.debug("No pricing data found in Supabase models")
|
257
|
+
return False
|
258
|
+
|
259
|
+
except Exception as e:
|
260
|
+
logger.debug(f"Failed to load pricing from Supabase: {e}")
|
261
|
+
return False
|
262
|
+
|
263
|
+
def _load_from_provider_configs(self) -> bool:
|
264
|
+
"""Load pricing data from provider configuration files"""
|
265
|
+
try:
|
266
|
+
providers_dir = self._find_project_root() / "isa_model" / "core" / "config" / "providers"
|
267
|
+
if not providers_dir.exists():
|
268
|
+
return False
|
269
|
+
|
270
|
+
self.pricing_data = {}
|
271
|
+
loaded_any = False
|
272
|
+
|
273
|
+
# Load all provider config files
|
274
|
+
for config_file in providers_dir.glob("*.yaml"):
|
275
|
+
if self._load_provider_config_file(config_file):
|
276
|
+
loaded_any = True
|
277
|
+
|
278
|
+
return loaded_any
|
279
|
+
|
280
|
+
except Exception as e:
|
281
|
+
logger.error(f"Failed to load pricing from provider configs: {e}")
|
282
|
+
return False
|
283
|
+
|
284
|
+
def _load_provider_config_file(self, config_file: Path) -> bool:
|
285
|
+
"""Load pricing data from a single provider config file"""
|
286
|
+
try:
|
287
|
+
with open(config_file, 'r') as f:
|
288
|
+
provider_data = yaml.safe_load(f)
|
289
|
+
|
290
|
+
provider_name = provider_data.get("provider")
|
291
|
+
if not provider_name:
|
292
|
+
return False
|
293
|
+
|
294
|
+
models = provider_data.get("models", [])
|
295
|
+
if not models:
|
296
|
+
return False
|
297
|
+
|
298
|
+
self.pricing_data[provider_name] = {}
|
299
|
+
|
300
|
+
for model in models:
|
301
|
+
model_id = model.get("model_id")
|
302
|
+
metadata = model.get("metadata", {})
|
303
|
+
|
304
|
+
if not model_id:
|
305
|
+
continue
|
306
|
+
|
307
|
+
# Extract pricing information from metadata
|
308
|
+
pricing = self._extract_pricing_from_metadata(metadata, provider_name, model_id)
|
309
|
+
if pricing:
|
310
|
+
self.pricing_data[provider_name][model_id] = pricing
|
311
|
+
|
312
|
+
logger.debug(f"Loaded pricing for {len(self.pricing_data[provider_name])} models from {provider_name}")
|
313
|
+
return True
|
314
|
+
|
315
|
+
except Exception as e:
|
316
|
+
logger.error(f"Failed to load provider config {config_file}: {e}")
|
317
|
+
return False
|
318
|
+
|
319
|
+
def _extract_pricing_from_metadata(self, metadata: Dict[str, Any], provider: str, model_name: str) -> Optional[ModelPricing]:
|
320
|
+
"""Extract pricing information from model metadata"""
|
321
|
+
try:
|
322
|
+
# Map different pricing field formats to our standard format
|
323
|
+
pricing_fields = {
|
324
|
+
"cost_per_1000_chars": ("character", 1000),
|
325
|
+
"cost_per_1000_tokens": ("token", 1000000), # Convert to cost per 1M tokens
|
326
|
+
"cost_per_minute": ("minute", 1),
|
327
|
+
"cost_per_image": ("image", 1),
|
328
|
+
"cost_per_request": ("request", 1),
|
329
|
+
}
|
330
|
+
|
331
|
+
input_cost = 0.0
|
332
|
+
output_cost = 0.0
|
333
|
+
unit_type = "token"
|
334
|
+
base_cost = 0.0
|
335
|
+
|
336
|
+
for field, (unit, multiplier) in pricing_fields.items():
|
337
|
+
if field in metadata:
|
338
|
+
cost = float(metadata[field])
|
339
|
+
if unit == "character":
|
340
|
+
# Convert cost per 1K chars to cost per 1K chars
|
341
|
+
input_cost = cost
|
342
|
+
unit_type = "character"
|
343
|
+
elif unit == "token":
|
344
|
+
# Cost per 1M tokens
|
345
|
+
input_cost = cost
|
346
|
+
unit_type = "token"
|
347
|
+
elif unit == "minute":
|
348
|
+
input_cost = cost
|
349
|
+
unit_type = "minute"
|
350
|
+
elif unit == "image":
|
351
|
+
input_cost = cost
|
352
|
+
unit_type = "image"
|
353
|
+
elif unit == "request":
|
354
|
+
base_cost = cost
|
355
|
+
break
|
356
|
+
|
357
|
+
# If no pricing found, skip this model
|
358
|
+
if input_cost == 0.0 and base_cost == 0.0:
|
359
|
+
return None
|
360
|
+
|
361
|
+
return ModelPricing(
|
362
|
+
provider=provider,
|
363
|
+
model_name=model_name,
|
364
|
+
input_cost=input_cost,
|
365
|
+
output_cost=output_cost,
|
366
|
+
unit_type=unit_type,
|
367
|
+
base_cost=base_cost,
|
368
|
+
last_updated=datetime.now()
|
369
|
+
)
|
370
|
+
|
371
|
+
except Exception as e:
|
372
|
+
logger.warning(f"Failed to extract pricing for {provider}/{model_name}: {e}")
|
373
|
+
return None
|
374
|
+
|
375
|
+
def _extract_pricing_from_supabase_metadata(self, metadata: Dict[str, Any], provider: str, model_name: str) -> Optional[ModelPricing]:
|
376
|
+
"""Extract pricing information from Supabase model metadata"""
|
377
|
+
try:
|
378
|
+
# Check for pricing information in metadata
|
379
|
+
pricing_info = metadata.get('pricing', {})
|
380
|
+
|
381
|
+
# If no pricing object, look for direct pricing fields
|
382
|
+
if not pricing_info:
|
383
|
+
# Look for various pricing field formats in metadata
|
384
|
+
pricing_fields = [
|
385
|
+
'cost_per_1000_chars', 'cost_per_1000_tokens', 'cost_per_minute',
|
386
|
+
'cost_per_image', 'cost_per_request', 'input_cost', 'output_cost',
|
387
|
+
'cost_per_1k_tokens', 'cost_per_1k_chars'
|
388
|
+
]
|
389
|
+
|
390
|
+
for field in pricing_fields:
|
391
|
+
if field in metadata:
|
392
|
+
# Create a pricing object from the field
|
393
|
+
if 'char' in field:
|
394
|
+
pricing_info = {'input': metadata[field], 'unit_type': 'character'}
|
395
|
+
elif 'token' in field:
|
396
|
+
pricing_info = {'input': metadata[field], 'unit_type': 'token'}
|
397
|
+
elif 'minute' in field:
|
398
|
+
pricing_info = {'input': metadata[field], 'unit_type': 'minute'}
|
399
|
+
elif 'image' in field:
|
400
|
+
pricing_info = {'input': metadata[field], 'unit_type': 'image'}
|
401
|
+
elif 'request' in field:
|
402
|
+
pricing_info = {'base_cost': metadata[field], 'unit_type': 'request'}
|
403
|
+
break
|
404
|
+
|
405
|
+
if not pricing_info:
|
406
|
+
return None
|
407
|
+
|
408
|
+
# Extract standard pricing fields
|
409
|
+
input_cost = float(pricing_info.get('input', pricing_info.get('input_cost', 0.0)))
|
410
|
+
output_cost = float(pricing_info.get('output', pricing_info.get('output_cost', 0.0)))
|
411
|
+
unit_type = pricing_info.get('unit_type', 'token')
|
412
|
+
base_cost = float(pricing_info.get('base_cost', 0.0))
|
413
|
+
infrastructure_cost_per_hour = float(pricing_info.get('infrastructure_cost_per_hour', 0.0))
|
414
|
+
currency = pricing_info.get('currency', 'USD')
|
415
|
+
|
416
|
+
# If no pricing found, skip this model
|
417
|
+
if input_cost == 0.0 and output_cost == 0.0 and base_cost == 0.0:
|
418
|
+
return None
|
419
|
+
|
420
|
+
return ModelPricing(
|
421
|
+
provider=provider,
|
422
|
+
model_name=model_name,
|
423
|
+
input_cost=input_cost,
|
424
|
+
output_cost=output_cost,
|
425
|
+
unit_type=unit_type,
|
426
|
+
base_cost=base_cost,
|
427
|
+
infrastructure_cost_per_hour=infrastructure_cost_per_hour,
|
428
|
+
currency=currency,
|
429
|
+
last_updated=datetime.now(),
|
430
|
+
metadata=pricing_info
|
431
|
+
)
|
432
|
+
|
433
|
+
except Exception as e:
|
434
|
+
logger.warning(f"Failed to extract pricing from Supabase metadata for {provider}/{model_name}: {e}")
|
435
|
+
return None
|
436
|
+
|
190
437
|
def get_model_pricing(self, provider: str, model_name: str) -> Optional[ModelPricing]:
|
191
438
|
"""Get pricing information for a specific model"""
|
192
439
|
self._refresh_if_needed()
|
@@ -140,6 +140,49 @@ class OllamaLLMService(BaseLLMService):
|
|
140
140
|
"""Use adapter manager to format response (consistent with OpenAI service)"""
|
141
141
|
return self.adapter_manager.format_response(response, original_input)
|
142
142
|
|
143
|
+
async def chat(
|
144
|
+
self,
|
145
|
+
input_data: Union[str, List[Dict[str, str]], Any],
|
146
|
+
max_tokens: Optional[int] = None
|
147
|
+
) -> Dict[str, Any]:
|
148
|
+
"""
|
149
|
+
Chat method that wraps ainvoke for compatibility with base class
|
150
|
+
|
151
|
+
Args:
|
152
|
+
input_data: Input messages
|
153
|
+
max_tokens: Maximum tokens to generate
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
Dict containing chat response with properly formatted message object
|
157
|
+
"""
|
158
|
+
try:
|
159
|
+
# Call ainvoke and get the response (already processed by adapter)
|
160
|
+
response = await self.ainvoke(input_data)
|
161
|
+
|
162
|
+
# Return the response as-is (adapter already formatted it correctly)
|
163
|
+
# For LangChain inputs, this will be an AIMessage object
|
164
|
+
# For standard inputs, this will be a string
|
165
|
+
return {
|
166
|
+
"message": response, # Use "message" to preserve object type
|
167
|
+
"success": True,
|
168
|
+
"metadata": {
|
169
|
+
"model": self.model_name,
|
170
|
+
"provider": self.provider_name,
|
171
|
+
"max_tokens": max_tokens or self.max_tokens
|
172
|
+
}
|
173
|
+
}
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"Chat method failed: {e}")
|
176
|
+
return {
|
177
|
+
"message": None,
|
178
|
+
"success": False,
|
179
|
+
"error": str(e),
|
180
|
+
"metadata": {
|
181
|
+
"model": self.model_name,
|
182
|
+
"provider": self.provider_name
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
143
186
|
|
144
187
|
async def _stream_response(self, payload: Dict[str, Any]) -> AsyncGenerator[str, None]:
|
145
188
|
"""Handle streaming responses"""
|
@@ -269,15 +269,17 @@ class OpenAILLMService(BaseLLMService):
|
|
269
269
|
max_tokens: Maximum tokens to generate
|
270
270
|
|
271
271
|
Returns:
|
272
|
-
Dict containing chat response
|
272
|
+
Dict containing chat response with properly formatted message object
|
273
273
|
"""
|
274
274
|
try:
|
275
|
-
# Call ainvoke and get the response
|
275
|
+
# Call ainvoke and get the response (already processed by adapter)
|
276
276
|
response = await self.ainvoke(input_data)
|
277
277
|
|
278
|
-
# Return
|
278
|
+
# Return the response as-is (adapter already formatted it correctly)
|
279
|
+
# For LangChain inputs, this will be an AIMessage object
|
280
|
+
# For standard inputs, this will be a string
|
279
281
|
return {
|
280
|
-
"
|
282
|
+
"message": response, # Changed from "text" to "message" to preserve object
|
281
283
|
"success": True,
|
282
284
|
"metadata": {
|
283
285
|
"model": self.model_name,
|
@@ -288,7 +290,7 @@ class OpenAILLMService(BaseLLMService):
|
|
288
290
|
except Exception as e:
|
289
291
|
logger.error(f"Chat method failed: {e}")
|
290
292
|
return {
|
291
|
-
"
|
293
|
+
"message": None,
|
292
294
|
"success": False,
|
293
295
|
"error": str(e),
|
294
296
|
"metadata": {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Dict, Any, List, Union, AsyncGenerator
|
2
|
+
from typing import Dict, Any, List, Union, AsyncGenerator, Optional
|
3
3
|
|
4
4
|
# (�� OpenAI �
|
5
5
|
from openai import AsyncOpenAI
|
@@ -249,6 +249,48 @@ class YydsLLMService(BaseLLMService):
|
|
249
249
|
}
|
250
250
|
}
|
251
251
|
|
252
|
+
async def chat(
|
253
|
+
self,
|
254
|
+
input_data: Union[str, List[Dict[str, str]], Any],
|
255
|
+
max_tokens: Optional[int] = None
|
256
|
+
) -> Dict[str, Any]:
|
257
|
+
"""
|
258
|
+
Chat method that wraps ainvoke for compatibility with base class
|
259
|
+
|
260
|
+
Args:
|
261
|
+
input_data: Input messages
|
262
|
+
max_tokens: Maximum tokens to generate
|
263
|
+
|
264
|
+
Returns:
|
265
|
+
Dict containing chat response with properly formatted message object
|
266
|
+
"""
|
267
|
+
try:
|
268
|
+
# Call ainvoke and get the response (already processed by adapter)
|
269
|
+
response = await self.ainvoke(input_data)
|
270
|
+
|
271
|
+
# Return the response as-is (adapter already formatted it correctly)
|
272
|
+
# For LangChain inputs, this will be an AIMessage object
|
273
|
+
# For standard inputs, this will be a string
|
274
|
+
return {
|
275
|
+
"message": response, # Use "message" to preserve object type
|
276
|
+
"success": True,
|
277
|
+
"metadata": {
|
278
|
+
"model": self.model_name,
|
279
|
+
"provider": self.provider_name,
|
280
|
+
"max_tokens": max_tokens or self.max_tokens
|
281
|
+
}
|
282
|
+
}
|
283
|
+
except Exception as e:
|
284
|
+
logger.error(f"Chat method failed: {e}")
|
285
|
+
return {
|
286
|
+
"message": None,
|
287
|
+
"success": False,
|
288
|
+
"error": str(e),
|
289
|
+
"metadata": {
|
290
|
+
"model": self.model_name,
|
291
|
+
"provider": self.provider_name
|
292
|
+
}
|
293
|
+
}
|
252
294
|
|
253
295
|
async def close(self):
|
254
296
|
"""Close the backend client"""
|
@@ -1,7 +1,7 @@
|
|
1
|
-
isa_model/__init__.py,sha256=
|
2
|
-
isa_model/client.py,sha256=
|
1
|
+
isa_model/__init__.py,sha256=73ibr8BcJyUmtEj-c86brXCY9BjZPYfnXpMbD1ashwQ,867
|
2
|
+
isa_model/client.py,sha256=xJt_wlaCZwv4fG053diFKMVpGCv3Qx0vGyFFPXow9y8,35862
|
3
3
|
isa_model/core/config.py,sha256=h9GVTEEMlaJYSCDd0W9q1KtaWTV5V5TawMsKtGuphds,15686
|
4
|
-
isa_model/core/pricing_manager.py,sha256=
|
4
|
+
isa_model/core/pricing_manager.py,sha256=W9VRHGsJVxs3EcPD2zRP0lvOYlxydb6E74RS-sLn_UA,27745
|
5
5
|
isa_model/core/types.py,sha256=XLUs442WGNc8E0gF2M-nb6dutD_s-XCfpr2BfGBCA2M,8445
|
6
6
|
isa_model/core/config/__init__.py,sha256=SLeHQtYGDHl64NDVyb3ECQXOKepGM8YNHEoM8CVEWus,350
|
7
7
|
isa_model/core/config/config_manager.py,sha256=hx0qcdvYEE_cCp-qb8tnVkXnpsTXEuRM1108DoAiUnE,19905
|
@@ -50,16 +50,6 @@ isa_model/inference/__init__.py,sha256=usfuQJ4zYY2RRtHkE-V6LuJ5aN7WJogtPUj9Qmy4W
|
|
50
50
|
isa_model/inference/ai_factory.py,sha256=oGtRd4wp6IZOTyI3GVKBNN4AtlnrLS7yFZuq2wvkaUg,19784
|
51
51
|
isa_model/inference/base.py,sha256=qwOddnSGI0GUdD6qIdGBPQpkW7UjU3Y-zaZvu70B4WA,1278
|
52
52
|
isa_model/inference/adapter/unified_api.py,sha256=67_Ok8W20m6Otf6r9WyOEVpnxondP4UAxOASk9ozDk4,8668
|
53
|
-
isa_model/inference/providers/__init__.py,sha256=a83q-LMFv8u47wf0XtxvqOw_mlVgA_90wtuwy02qdDE,581
|
54
|
-
isa_model/inference/providers/base_provider.py,sha256=PT-YnGwBu-Jn_4T3iAphkAJw_mYmKVLjUID62vf2_Ow,2711
|
55
|
-
isa_model/inference/providers/ml_provider.py,sha256=4oGGF7lVWQ91Qh3h7olyPFoACLxCROaMxUZlDiZrRL4,1661
|
56
|
-
isa_model/inference/providers/modal_provider.py,sha256=klRYXECD5TJicodHIElsGNGMAsAWRFhvn4yfCRcqdVs,3654
|
57
|
-
isa_model/inference/providers/model_cache_manager.py,sha256=dLRpx7OJweQ5LcSAkU7D0DQRfLtIhG6nGvg4W_gau80,15315
|
58
|
-
isa_model/inference/providers/ollama_provider.py,sha256=IfM9XhdzfE1faguzS2-4GfhK30v5kDPecD3l4z2eB1w,3620
|
59
|
-
isa_model/inference/providers/openai_provider.py,sha256=tB8FMsMivlRx0cfPJ0Yrxh1DCvuXyyjNFXrO4lMkkhA,5366
|
60
|
-
isa_model/inference/providers/replicate_provider.py,sha256=0oi_BglIE6-HYgzLau9ifP8OdpAMO-QkwYk0OXRUzPk,4490
|
61
|
-
isa_model/inference/providers/triton_provider.py,sha256=GKlth7cTOx6ERbsXXJ0gDNby3kVGQNULBDt098BXBSU,15258
|
62
|
-
isa_model/inference/providers/yyds_provider.py,sha256=KbDsopShs11_G9oX3b2i2NgHIqkZV7HYWe9K9uZLccc,4284
|
63
53
|
isa_model/inference/services/__init__.py,sha256=yfLz0YGl8ixk6LfTRL6cRTvZMb9F_Pv1QRgGyNc9xYM,386
|
64
54
|
isa_model/inference/services/base_service.py,sha256=fVaSx0CogHK71UEsNJeSyM8mhqmq5_9ePbbSZVi3Al8,5085
|
65
55
|
isa_model/inference/services/audio/base_stt_service.py,sha256=sfzAfreFdvEOBHtphoTrQSjb-gCoCOW4WCj6iIe51oU,5804
|
@@ -80,9 +70,9 @@ isa_model/inference/services/img/replicate_image_gen_service.py,sha256=QLjgrXNmt
|
|
80
70
|
isa_model/inference/services/img/helpers/base_stacked_service.py,sha256=Y2g2Hz3n2Uwo4RoCbsC33IXxTeQ2TeT6T-2HnXd-2B0,10546
|
81
71
|
isa_model/inference/services/llm/__init__.py,sha256=hSdfeqE0463CtQ6ZzREqPBvtzXNuzi3W5PFKS0nwhvo,336
|
82
72
|
isa_model/inference/services/llm/base_llm_service.py,sha256=fg79J5mKy27qahvtLscMcquOe4O2L-EoNjE33yPRI9E,22196
|
83
|
-
isa_model/inference/services/llm/ollama_llm_service.py,sha256=
|
84
|
-
isa_model/inference/services/llm/openai_llm_service.py,sha256=
|
85
|
-
isa_model/inference/services/llm/yyds_llm_service.py,sha256=
|
73
|
+
isa_model/inference/services/llm/ollama_llm_service.py,sha256=PfsMesjdtpqseLa2OUUg8WWyiIdIUhu9L5s5DkZl040,17580
|
74
|
+
isa_model/inference/services/llm/openai_llm_service.py,sha256=40FYFb0bnVQrPwTg10CAKytKDj4zeZvp517do00Y_Ok,12174
|
75
|
+
isa_model/inference/services/llm/yyds_llm_service.py,sha256=I8fBCd1dk56hlpnkToh-aAE77hP5YanHRVVEcXHYasE,12051
|
86
76
|
isa_model/inference/services/llm/helpers/llm_adapter.py,sha256=gkKND55EoizRxsaGOCEazUmL-2yTIBI-_njpEOXt-4k,21779
|
87
77
|
isa_model/inference/services/llm/helpers/llm_prompts.py,sha256=4ZOovr_Jx5bwPiMkgO1lZF7GMyEFbITP3ZrhhI48QMs,10964
|
88
78
|
isa_model/inference/services/llm/helpers/llm_utils.py,sha256=LNfB3qzREyD2TyleOyVa20FU8HbhdxA8G6QV-N-wfZs,10016
|
@@ -142,7 +132,7 @@ isa_model/training/core/config.py,sha256=oqgKpBvtzrN6jwLIQYQ2707lH6nmjrktRiSxp9i
|
|
142
132
|
isa_model/training/core/dataset.py,sha256=XCFsnf0NUMU1dJpdvo_CAMyvXB-9_RCUEiy8TU50e20,7802
|
143
133
|
isa_model/training/core/trainer.py,sha256=h5TjqjdFr0Fsv5y4-0siy1KmOlqLfliVaUXybvuoeXU,26932
|
144
134
|
isa_model/training/core/utils.py,sha256=Nik0M2ssfNbWqP6fKO0Kfyhzr_H6Q19ioxB-qCYbn5E,8387
|
145
|
-
isa_model-0.3.
|
146
|
-
isa_model-0.3.
|
147
|
-
isa_model-0.3.
|
148
|
-
isa_model-0.3.
|
135
|
+
isa_model-0.3.9.dist-info/METADATA,sha256=SAC2G5ZmyJ0KvNcE_8ONOGCI53bx691wWFAwb-KQghU,12326
|
136
|
+
isa_model-0.3.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
137
|
+
isa_model-0.3.9.dist-info/top_level.txt,sha256=eHSy_Xb3kNkh2kK11mi1mZh0Wz91AQ5b8k2KFYO-rE8,10
|
138
|
+
isa_model-0.3.9.dist-info/RECORD,,
|
@@ -1,19 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Providers - Components for integrating with different model providers
|
3
|
-
|
4
|
-
File: isa_model/inference/providers/__init__.py
|
5
|
-
This module contains provider implementations for different AI model backends.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from .base_provider import BaseProvider
|
9
|
-
|
10
|
-
__all__ = [
|
11
|
-
"BaseProvider",
|
12
|
-
]
|
13
|
-
|
14
|
-
# Provider implementations can be imported individually as needed
|
15
|
-
# from .triton_provider import TritonProvider
|
16
|
-
# from .ollama_provider import OllamaProvider
|
17
|
-
# from .yyds_provider import YYDSProvider
|
18
|
-
# from .openai_provider import OpenAIProvider
|
19
|
-
# from .replicate_provider import ReplicateProvider
|