mojentic 0.7.4__py3-none-any.whl → 0.8.0__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.
@@ -14,7 +14,7 @@ from mojentic.llm.gateways.models import LLMMessage
14
14
  from mojentic.llm.tools.date_resolver import ResolveDateTool
15
15
 
16
16
 
17
- def openai_llm(model="gpt-4o"):
17
+ def openai_llm(model="gpt-5"):
18
18
  api_key = os.getenv("OPENAI_API_KEY")
19
19
  gateway = OpenAIGateway(api_key)
20
20
  llm = LLMBroker(model=model, gateway=gateway)
@@ -60,7 +60,26 @@ check_structured_output(openai_llm(model="o4-mini"))
60
60
  check_tool_use(openai_llm(model="o4-mini"))
61
61
  check_image_analysis(openai_llm(model="gpt-4o"))
62
62
 
63
- check_simple_textgen(ollama_llm())
64
- check_structured_output(ollama_llm())
63
+ # check_simple_textgen(ollama_llm())
64
+ # check_structured_output(ollama_llm())
65
65
  check_tool_use(ollama_llm(model="qwen3:32b"))
66
66
  check_image_analysis(ollama_llm(model="gemma3:27b"))
67
+
68
+ # Test all GPT-5 model variants to confirm they're all reasoning models
69
+ print("\n=== Testing GPT-5 Model Variants ===")
70
+ gpt5_models = [
71
+ "gpt-5",
72
+ "gpt-5-2025-08-07",
73
+ "gpt-5-chat-latest",
74
+ "gpt-5-mini",
75
+ "gpt-5-mini-2025-08-07",
76
+ "gpt-5-nano",
77
+ "gpt-5-nano-2025-08-07"
78
+ ]
79
+
80
+ for model in gpt5_models:
81
+ print(f"\n--- Testing {model} ---")
82
+ try:
83
+ check_simple_textgen(openai_llm(model=model))
84
+ except Exception as e:
85
+ print(f"Error with {model}: {e}")
@@ -0,0 +1,104 @@
1
+ """
2
+ Script to fetch current OpenAI models and update the registry with up-to-date model lists.
3
+ """
4
+
5
+ import os
6
+ from mojentic.llm.gateways.openai import OpenAIGateway
7
+
8
+ def fetch_current_openai_models():
9
+ """Fetch the current list of OpenAI models."""
10
+ api_key = os.getenv("OPENAI_API_KEY")
11
+ if not api_key:
12
+ print("ERROR: OPENAI_API_KEY environment variable not set")
13
+ return None
14
+
15
+ try:
16
+ gateway = OpenAIGateway(api_key)
17
+ models = gateway.get_available_models()
18
+ return models
19
+ except Exception as e:
20
+ print(f"ERROR: Failed to fetch models from OpenAI API: {e}")
21
+ return None
22
+
23
+ def categorize_models(models):
24
+ """Categorize models by type based on naming patterns."""
25
+ reasoning_models = []
26
+ chat_models = []
27
+ embedding_models = []
28
+ other_models = []
29
+
30
+ for model in models:
31
+ model_lower = model.lower()
32
+
33
+ # Reasoning models: o1, o3, o4, and gpt-5 series
34
+ if (any(pattern in model_lower for pattern in ['o1-', 'o3-', 'o4-', 'gpt-5']) or
35
+ model_lower in ['o1', 'o3', 'o4', 'gpt-5']):
36
+ reasoning_models.append(model)
37
+ elif 'embedding' in model_lower:
38
+ embedding_models.append(model)
39
+ elif any(pattern in model_lower for pattern in ['gpt-4', 'gpt-3.5']):
40
+ chat_models.append(model)
41
+ else:
42
+ other_models.append(model)
43
+
44
+ return {
45
+ 'reasoning': sorted(reasoning_models),
46
+ 'chat': sorted(chat_models),
47
+ 'embedding': sorted(embedding_models),
48
+ 'other': sorted(other_models)
49
+ }
50
+
51
+ def print_model_lists(categorized_models):
52
+ """Print the categorized models in a format ready for the registry."""
53
+ print("=== Current OpenAI Models ===\n")
54
+
55
+ print("# Reasoning Models (o1, o3, o4, gpt-5 series)")
56
+ print("reasoning_models = [")
57
+ for model in categorized_models['reasoning']:
58
+ print(f' "{model}",')
59
+ print("]\n")
60
+
61
+ print("# Chat Models (GPT-4 and GPT-4.1 series)")
62
+ print("gpt4_and_newer_models = [")
63
+ gpt4_and_newer = [m for m in categorized_models['chat'] if 'gpt-4' in m.lower()]
64
+ for model in gpt4_and_newer:
65
+ print(f' "{model}",')
66
+ print("]\n")
67
+
68
+ print("# Chat Models (GPT-3.5 series)")
69
+ print("gpt35_models = [")
70
+ gpt35 = [m for m in categorized_models['chat'] if 'gpt-3.5' in m.lower()]
71
+ for model in gpt35:
72
+ print(f' "{model}",')
73
+ print("]\n")
74
+
75
+ print("# Embedding Models")
76
+ print("embedding_models = [")
77
+ for model in categorized_models['embedding']:
78
+ print(f' "{model}",')
79
+ print("]\n")
80
+
81
+ print("# Other Models (for reference)")
82
+ print("# other_models = [")
83
+ for model in categorized_models['other']:
84
+ print(f'# "{model}",')
85
+ print("# ]\n")
86
+
87
+ if __name__ == "__main__":
88
+ print("Fetching current OpenAI models...")
89
+ models = fetch_current_openai_models()
90
+
91
+ if models:
92
+ print(f"Found {len(models)} models\n")
93
+ categorized = categorize_models(models)
94
+ print_model_lists(categorized)
95
+
96
+ print("\n=== Summary ===")
97
+ print(f"Reasoning models: {len(categorized['reasoning'])}")
98
+ print(f"Chat models: {len(categorized['chat'])}")
99
+ print(f"Embedding models: {len(categorized['embedding'])}")
100
+ print(f"Other models: {len(categorized['other'])}")
101
+
102
+ print("\nCopy the model lists above and update the _initialize_default_models() method in openai_model_registry.py")
103
+ else:
104
+ print("Failed to fetch models. Please check your API key and try again.")
@@ -0,0 +1,140 @@
1
+ """
2
+ Demonstration of the enhanced OpenAI gateway with model registry system.
3
+
4
+ This script shows how the new infrastructure automatically handles parameter adaptation
5
+ for reasoning models vs chat models, provides detailed logging, and offers better
6
+ error handling.
7
+ """
8
+
9
+ import os
10
+ from mojentic.llm.gateways.openai import OpenAIGateway
11
+ from mojentic.llm.gateways.openai_model_registry import get_model_registry
12
+ from mojentic.llm.gateways.models import LLMMessage, MessageRole
13
+
14
+ def demonstrate_model_registry():
15
+ """Demonstrate the model registry capabilities."""
16
+ print("=== Model Registry Demonstration ===")
17
+
18
+ registry = get_model_registry()
19
+
20
+ print("\n1. Registry contains default models:")
21
+ registered_models = registry.get_registered_models()
22
+ reasoning_models = [m for m in registered_models if registry.is_reasoning_model(m)]
23
+ chat_models = [m for m in registered_models if not registry.is_reasoning_model(m) and not m.startswith('text-')]
24
+
25
+ print(f" Reasoning models: {reasoning_models[:3]}...") # Show first 3
26
+ print(f" Chat models: {chat_models[:3]}...") # Show first 3
27
+
28
+ print("\n2. Model capability detection:")
29
+ for model in ["o1-mini", "gpt-4o"]:
30
+ capabilities = registry.get_model_capabilities(model)
31
+ token_param = capabilities.get_token_limit_param()
32
+ print(f" {model}: type={capabilities.model_type.value}, token_param={token_param}")
33
+
34
+ # Handle unknown model separately to show the warning works
35
+ print("\n3. Unknown model handling:")
36
+ print(" unknown-future-model: (will default to chat model with warning)")
37
+ capabilities = registry.get_model_capabilities("unknown-future-model")
38
+ token_param = capabilities.get_token_limit_param()
39
+ print(f" → Defaulted to: type={capabilities.model_type.value}, token_param={token_param}")
40
+
41
+ def demonstrate_parameter_adaptation():
42
+ """Demonstrate parameter adaptation for different model types."""
43
+ print("\n=== Parameter Adaptation Demonstration ===")
44
+
45
+ # This would normally require an API key, but we're just showing the adaptation logic
46
+ gateway = OpenAIGateway("fake-key-for-demo")
47
+
48
+ print("\n1. Reasoning model parameter adaptation (o1-mini):")
49
+ original_args = {
50
+ 'model': 'o1-mini',
51
+ 'messages': [LLMMessage(role=MessageRole.User, content="Hello")],
52
+ 'max_tokens': 1000,
53
+ 'tools': [] # Tools will be removed for reasoning models
54
+ }
55
+
56
+ adapted_args = gateway._adapt_parameters_for_model('o1-mini', original_args)
57
+ print(f" Original: max_tokens={original_args.get('max_tokens')}, has_tools={'tools' in original_args}")
58
+ print(f" Adapted: max_completion_tokens={adapted_args.get('max_completion_tokens')}, has_tools={'tools' in adapted_args}")
59
+
60
+ print("\n2. Chat model parameter adaptation (gpt-4o):")
61
+ original_args = {
62
+ 'model': 'gpt-4o',
63
+ 'messages': [LLMMessage(role=MessageRole.User, content="Hello")],
64
+ 'max_tokens': 1000,
65
+ 'tools': []
66
+ }
67
+
68
+ adapted_args = gateway._adapt_parameters_for_model('gpt-4o', original_args)
69
+ print(f" Original: max_tokens={original_args.get('max_tokens')}, has_tools={'tools' in original_args}")
70
+ print(f" Adapted: max_tokens={adapted_args.get('max_tokens')}, has_tools={'tools' in adapted_args}")
71
+
72
+ def demonstrate_model_validation():
73
+ """Demonstrate model parameter validation."""
74
+ print("\n=== Model Validation Demonstration ===")
75
+
76
+ gateway = OpenAIGateway("fake-key-for-demo")
77
+
78
+ print("\n1. Validating parameters for reasoning model:")
79
+ args = {
80
+ 'model': 'o1-mini',
81
+ 'messages': [LLMMessage(role=MessageRole.User, content="Hello")],
82
+ 'max_tokens': 50000, # High token count - will show warning
83
+ 'tools': [] # Tools for reasoning model - will show warning
84
+ }
85
+
86
+ try:
87
+ gateway._validate_model_parameters('o1-mini', args)
88
+ print(" Validation completed (check logs above for warnings)")
89
+ except Exception as e:
90
+ print(f" Validation error: {e}")
91
+
92
+ def demonstrate_registry_extensibility():
93
+ """Demonstrate how to extend the registry with new models."""
94
+ print("\n=== Registry Extensibility Demonstration ===")
95
+
96
+ registry = get_model_registry()
97
+
98
+ print("\n1. Adding a new model to the registry:")
99
+ from mojentic.llm.gateways.openai_model_registry import ModelCapabilities, ModelType
100
+
101
+ new_capabilities = ModelCapabilities(
102
+ model_type=ModelType.REASONING,
103
+ supports_tools=True, # Hypothetical future reasoning model with tools
104
+ supports_streaming=True,
105
+ max_output_tokens=100000
106
+ )
107
+
108
+ registry.register_model("o5-preview", new_capabilities)
109
+ print(f" Registered o5-preview as reasoning model")
110
+
111
+ # Test the new model
112
+ capabilities = registry.get_model_capabilities("o5-preview")
113
+ print(f" o5-preview: type={capabilities.model_type.value}, supports_tools={capabilities.supports_tools}")
114
+
115
+ print("\n2. Adding a new pattern for model detection:")
116
+ registry.register_pattern("claude", ModelType.CHAT)
117
+ print(" Registered 'claude' pattern for chat models")
118
+
119
+ # Test pattern matching
120
+ capabilities = registry.get_model_capabilities("claude-3-opus")
121
+ print(f" claude-3-opus (inferred): type={capabilities.model_type.value}")
122
+
123
+ if __name__ == "__main__":
124
+ print("OpenAI Gateway Enhanced Infrastructure Demo")
125
+ print("=" * 50)
126
+
127
+ demonstrate_model_registry()
128
+ demonstrate_parameter_adaptation()
129
+ demonstrate_model_validation()
130
+ demonstrate_registry_extensibility()
131
+
132
+ print("\n" + "=" * 50)
133
+ print("Demo completed!")
134
+ print("\nKey Benefits of the New Infrastructure:")
135
+ print("✓ Registry-based model management (easy to extend)")
136
+ print("✓ Automatic parameter adaptation (max_tokens ↔ max_completion_tokens)")
137
+ print("✓ Enhanced logging for debugging")
138
+ print("✓ Parameter validation with helpful warnings")
139
+ print("✓ Pattern matching for unknown models")
140
+ print("✓ Comprehensive test coverage")
@@ -1,15 +1,18 @@
1
1
  import json
2
2
  from itertools import islice
3
- from typing import Type, List, Iterable
3
+ from typing import Type, List, Iterable, Optional
4
4
 
5
5
  import numpy as np
6
6
  import structlog
7
- from openai import OpenAI
7
+ from openai import OpenAI, BadRequestError
8
+ from pydantic import BaseModel
8
9
 
9
10
  from mojentic.llm.gateways.llm_gateway import LLMGateway
10
- from mojentic.llm.gateways.models import LLMToolCall, LLMGatewayResponse
11
+ from mojentic.llm.gateways.models import LLMToolCall, LLMGatewayResponse, LLMMessage
11
12
  from mojentic.llm.gateways.openai_messages_adapter import adapt_messages_to_openai
13
+ from mojentic.llm.gateways.openai_model_registry import get_model_registry, ModelType
12
14
  from mojentic.llm.gateways.tokenizer_gateway import TokenizerGateway
15
+ from mojentic.llm.tools.llm_tool import LLMTool
13
16
 
14
17
  logger = structlog.get_logger()
15
18
 
@@ -24,8 +27,9 @@ class OpenAIGateway(LLMGateway):
24
27
  The OpenAI API key to use.
25
28
  """
26
29
 
27
- def __init__(self, api_key: str, base_url: str = None):
30
+ def __init__(self, api_key: str, base_url: Optional[str] = None):
28
31
  self.client = OpenAI(api_key=api_key, base_url=base_url)
32
+ self.model_registry = get_model_registry()
29
33
 
30
34
  def _is_reasoning_model(self, model: str) -> bool:
31
35
  """
@@ -41,21 +45,11 @@ class OpenAIGateway(LLMGateway):
41
45
  bool
42
46
  True if the model is a reasoning model, False if it's a chat model.
43
47
  """
44
- # OpenAI reasoning models typically start with "o1" or contain "o4"
45
- reasoning_model_patterns = [
46
- "o1-",
47
- "o3-",
48
- "o4-",
49
- "o1",
50
- "o3"
51
- ]
52
-
53
- model_lower = model.lower()
54
- return any(pattern in model_lower for pattern in reasoning_model_patterns)
48
+ return self.model_registry.is_reasoning_model(model)
55
49
 
56
50
  def _adapt_parameters_for_model(self, model: str, args: dict) -> dict:
57
51
  """
58
- Adapt parameters based on the model type.
52
+ Adapt parameters based on the model type and capabilities.
59
53
 
60
54
  Parameters
61
55
  ----------
@@ -70,26 +64,80 @@ class OpenAIGateway(LLMGateway):
70
64
  The adapted arguments with correct parameter names for the model type.
71
65
  """
72
66
  adapted_args = args.copy()
67
+ capabilities = self.model_registry.get_model_capabilities(model)
73
68
 
74
- if self._is_reasoning_model(model) and 'max_tokens' in adapted_args:
75
- # For reasoning models, use max_completion_tokens instead of max_tokens
76
- adapted_args['max_completion_tokens'] = adapted_args.pop('max_tokens')
77
- logger.debug("Adapted max_tokens to max_completion_tokens for reasoning model",
78
- model=model, max_completion_tokens=adapted_args['max_completion_tokens'])
69
+ logger.debug("Adapting parameters for model",
70
+ model=model,
71
+ model_type=capabilities.model_type.value,
72
+ supports_tools=capabilities.supports_tools,
73
+ supports_streaming=capabilities.supports_streaming)
74
+
75
+ # Handle token limit parameter conversion
76
+ if 'max_tokens' in adapted_args:
77
+ token_param = capabilities.get_token_limit_param()
78
+ if token_param != 'max_tokens':
79
+ # Convert max_tokens to max_completion_tokens for reasoning models
80
+ adapted_args[token_param] = adapted_args.pop('max_tokens')
81
+ logger.info("Converted token limit parameter for model",
82
+ model=model,
83
+ from_param='max_tokens',
84
+ to_param=token_param,
85
+ value=adapted_args[token_param])
86
+
87
+ # Validate tool usage for models that don't support tools
88
+ if 'tools' in adapted_args and adapted_args['tools'] and not capabilities.supports_tools:
89
+ logger.warning("Model does not support tools, removing tool configuration",
90
+ model=model,
91
+ num_tools=len(adapted_args['tools']))
92
+ adapted_args['tools'] = None # Set to None instead of removing the key
79
93
 
80
94
  return adapted_args
81
95
 
82
- def complete(self, **args) -> LLMGatewayResponse:
96
+ def _validate_model_parameters(self, model: str, args: dict) -> None:
97
+ """
98
+ Validate that the parameters are compatible with the model.
99
+
100
+ Parameters
101
+ ----------
102
+ model : str
103
+ The model name.
104
+ args : dict
105
+ The arguments to validate.
106
+ """
107
+ capabilities = self.model_registry.get_model_capabilities(model)
108
+
109
+ # Warning for tools on reasoning models that don't support them
110
+ if (capabilities.model_type == ModelType.REASONING and
111
+ not capabilities.supports_tools and
112
+ 'tools' in args and args['tools']):
113
+ logger.warning(
114
+ "Reasoning model may not support tools",
115
+ model=model,
116
+ num_tools=len(args['tools'])
117
+ )
118
+
119
+ # Validate token limits (check both possible parameter names)
120
+ token_value = args.get('max_tokens') or args.get('max_completion_tokens')
121
+ if token_value and capabilities.max_output_tokens:
122
+ if token_value > capabilities.max_output_tokens:
123
+ logger.warning(
124
+ "Requested token limit exceeds model maximum",
125
+ model=model,
126
+ requested=token_value,
127
+ max_allowed=capabilities.max_output_tokens
128
+ )
129
+
130
+ def complete(self, **kwargs) -> LLMGatewayResponse:
83
131
  """
84
132
  Complete the LLM request by delegating to the OpenAI service.
85
133
 
86
134
  Keyword Arguments
87
135
  ----------------
88
136
  model : str
89
- The name of the model to use, as appears in `ollama list`.
137
+ The name of the model to use.
90
138
  messages : List[LLMMessage]
91
139
  A list of messages to send to the LLM.
92
- object_model : Optional[BaseModel]
140
+ object_model : Optional[Type[BaseModel]]
93
141
  The model to use for validating the response.
94
142
  tools : Optional[List[LLMTool]]
95
143
  A list of tools to use with the LLM. If a tool call is requested, the tool will be called and the output
@@ -108,21 +156,61 @@ class OpenAIGateway(LLMGateway):
108
156
  LLMGatewayResponse
109
157
  The response from the OpenAI service.
110
158
  """
159
+ # Extract parameters from kwargs with defaults
160
+ model = kwargs.get('model')
161
+ messages = kwargs.get('messages')
162
+ object_model = kwargs.get('object_model', None)
163
+ tools = kwargs.get('tools', None)
164
+ temperature = kwargs.get('temperature', 1.0)
165
+ num_ctx = kwargs.get('num_ctx', 32768)
166
+ max_tokens = kwargs.get('max_tokens', 16384)
167
+ num_predict = kwargs.get('num_predict', -1)
168
+
169
+ if not model:
170
+ raise ValueError("'model' parameter is required")
171
+ if not messages:
172
+ raise ValueError("'messages' parameter is required")
173
+
174
+ # Convert parameters to dict for processing
175
+ args = {
176
+ 'model': model,
177
+ 'messages': messages,
178
+ 'object_model': object_model,
179
+ 'tools': tools,
180
+ 'temperature': temperature,
181
+ 'num_ctx': num_ctx,
182
+ 'max_tokens': max_tokens,
183
+ 'num_predict': num_predict
184
+ }
185
+
111
186
  # Adapt parameters based on model type
112
- adapted_args = self._adapt_parameters_for_model(args['model'], args)
187
+ try:
188
+ adapted_args = self._adapt_parameters_for_model(model, args)
189
+ except Exception as e:
190
+ logger.error("Failed to adapt parameters for model",
191
+ model=model,
192
+ error=str(e))
193
+ raise
194
+
195
+ # Validate parameters after adaptation
196
+ self._validate_model_parameters(model, adapted_args)
113
197
 
114
198
  openai_args = {
115
199
  'model': adapted_args['model'],
116
200
  'messages': adapt_messages_to_openai(adapted_args['messages']),
117
201
  }
118
202
 
203
+ # Add temperature if specified
204
+ if 'temperature' in adapted_args:
205
+ openai_args['temperature'] = adapted_args['temperature']
206
+
119
207
  completion = self.client.chat.completions.create
120
208
 
121
- if 'object_model' in adapted_args and adapted_args['object_model'] is not None:
209
+ if adapted_args['object_model'] is not None:
122
210
  completion = self.client.beta.chat.completions.parse
123
211
  openai_args['response_format'] = adapted_args['object_model']
124
212
 
125
- if 'tools' in adapted_args and adapted_args['tools'] is not None:
213
+ if adapted_args.get('tools') is not None:
126
214
  openai_args['tools'] = [t.descriptor for t in adapted_args['tools']]
127
215
 
128
216
  # Handle both max_tokens (for chat models) and max_completion_tokens (for reasoning models)
@@ -131,18 +219,42 @@ class OpenAIGateway(LLMGateway):
131
219
  elif 'max_completion_tokens' in adapted_args:
132
220
  openai_args['max_completion_tokens'] = adapted_args['max_completion_tokens']
133
221
 
134
- response = completion(**openai_args)
222
+ logger.debug("Making OpenAI API call",
223
+ model=openai_args['model'],
224
+ has_tools='tools' in openai_args,
225
+ has_object_model='response_format' in openai_args,
226
+ token_param='max_completion_tokens' if 'max_completion_tokens' in openai_args else 'max_tokens')
227
+
228
+ try:
229
+ response = completion(**openai_args)
230
+ except BadRequestError as e:
231
+ # Enhanced error handling for parameter issues
232
+ if "max_tokens" in str(e) and "max_completion_tokens" in str(e):
233
+ logger.error("Parameter error detected - model may require different token parameter",
234
+ model=model,
235
+ error=str(e),
236
+ suggestion="This model may be a reasoning model requiring max_completion_tokens")
237
+ raise e
238
+ except Exception as e:
239
+ logger.error("OpenAI API call failed",
240
+ model=model,
241
+ error=str(e))
242
+ raise e
135
243
 
136
244
  object = None
137
245
  tool_calls: List[LLMToolCall] = []
138
246
 
139
- if 'object_model' in args and args['object_model'] is not None:
247
+ if adapted_args.get('object_model') is not None:
140
248
  try:
141
249
  response_content = response.choices[0].message.content
142
- object = args['object_model'].model_validate_json(response_content)
250
+ if response_content is not None:
251
+ object = adapted_args['object_model'].model_validate_json(response_content)
252
+ else:
253
+ logger.error("No response content available for object validation", object_model=adapted_args['object_model'])
143
254
  except Exception as e:
255
+ response_content = response.choices[0].message.content if response.choices else "No response content"
144
256
  logger.error("Failed to validate model", error=str(e), response=response_content,
145
- object_model=args['object_model'])
257
+ object_model=adapted_args['object_model'])
146
258
 
147
259
  if response.choices[0].message.tool_calls is not None:
148
260
  for t in response.choices[0].message.tool_calls:
@@ -0,0 +1,327 @@
1
+ """
2
+ OpenAI Model Registry for managing model-specific configurations and capabilities.
3
+
4
+ This module provides infrastructure for categorizing OpenAI models and managing
5
+ their specific parameter requirements and capabilities.
6
+ """
7
+
8
+ from enum import Enum
9
+ from typing import Dict, Set, Optional, List, TYPE_CHECKING
10
+ from dataclasses import dataclass
11
+
12
+ import structlog
13
+
14
+ if TYPE_CHECKING:
15
+ from mojentic.llm.gateways.openai import OpenAIGateway
16
+
17
+ logger = structlog.get_logger()
18
+
19
+
20
+ class ModelType(Enum):
21
+ """Classification of OpenAI model types based on their capabilities and parameters."""
22
+ REASONING = "reasoning" # Models like o1, o3 that use max_completion_tokens
23
+ CHAT = "chat" # Standard chat models that use max_tokens
24
+ EMBEDDING = "embedding" # Text embedding models
25
+ MODERATION = "moderation" # Content moderation models
26
+
27
+
28
+ @dataclass
29
+ class ModelCapabilities:
30
+ """Defines the capabilities and parameter requirements for a model."""
31
+ model_type: ModelType
32
+ supports_tools: bool = True
33
+ supports_streaming: bool = True
34
+ supports_vision: bool = False
35
+ max_context_tokens: Optional[int] = None
36
+ max_output_tokens: Optional[int] = None
37
+
38
+ def get_token_limit_param(self) -> str:
39
+ """Get the correct parameter name for token limits based on model type."""
40
+ if self.model_type == ModelType.REASONING:
41
+ return "max_completion_tokens"
42
+ return "max_tokens"
43
+
44
+
45
+ class OpenAIModelRegistry:
46
+ """
47
+ Registry for managing OpenAI model configurations and capabilities.
48
+
49
+ This class provides a centralized way to manage model-specific configurations,
50
+ parameter mappings, and capabilities for OpenAI models.
51
+ """
52
+
53
+ def __init__(self):
54
+ self._models: Dict[str, ModelCapabilities] = {}
55
+ self._pattern_mappings: Dict[str, ModelType] = {}
56
+ self._initialize_default_models()
57
+
58
+ def _initialize_default_models(self):
59
+ """Initialize the registry with known OpenAI models and their capabilities."""
60
+
61
+ # Reasoning Models (o1, o3, o4, gpt-5 series) - Updated 2025-09-28
62
+ reasoning_models = [
63
+ "o1", "o1-2024-12-17", "o1-mini", "o1-mini-2024-09-12",
64
+ "o1-pro", "o1-pro-2025-03-19",
65
+ "o3", "o3-2025-04-16", "o3-deep-research", "o3-deep-research-2025-06-26",
66
+ "o3-mini", "o3-mini-2025-01-31", "o3-pro", "o3-pro-2025-06-10",
67
+ "o4-mini", "o4-mini-2025-04-16", "o4-mini-deep-research",
68
+ "o4-mini-deep-research-2025-06-26",
69
+ "gpt-5", "gpt-5-2025-08-07", "gpt-5-chat-latest", "gpt-5-codex",
70
+ "gpt-5-mini", "gpt-5-mini-2025-08-07", "gpt-5-nano", "gpt-5-nano-2025-08-07"
71
+ ]
72
+
73
+ for model in reasoning_models:
74
+ # Deep research models and GPT-5 might have different capabilities
75
+ is_deep_research = "deep-research" in model
76
+ is_gpt5 = "gpt-5" in model
77
+ is_mini_or_nano = ("mini" in model or "nano" in model)
78
+
79
+ # GPT-5 models may support more features than o1/o3/o4
80
+ supports_tools = is_gpt5 # GPT-5 might support tools
81
+ supports_streaming = is_gpt5 # GPT-5 might support streaming
82
+
83
+ # Set context and output tokens based on model tier
84
+ if is_gpt5:
85
+ context_tokens = 300000 if not is_mini_or_nano else 200000
86
+ output_tokens = 50000 if not is_mini_or_nano else 32768
87
+ elif is_deep_research:
88
+ context_tokens = 200000
89
+ output_tokens = 100000
90
+ else:
91
+ context_tokens = 128000
92
+ output_tokens = 32768
93
+
94
+ self._models[model] = ModelCapabilities(
95
+ model_type=ModelType.REASONING,
96
+ supports_tools=supports_tools,
97
+ supports_streaming=supports_streaming,
98
+ supports_vision=False, # Vision support would need to be confirmed for GPT-5
99
+ max_context_tokens=context_tokens,
100
+ max_output_tokens=output_tokens
101
+ )
102
+
103
+ # Chat Models (GPT-4 and GPT-4.1 series) - Updated 2025-09-28
104
+ # Note: GPT-5 series moved to reasoning models
105
+ gpt4_and_newer_models = [
106
+ "chatgpt-4o-latest",
107
+ "gpt-4", "gpt-4-0125-preview", "gpt-4-0613", "gpt-4-1106-preview",
108
+ "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-turbo-preview",
109
+ "gpt-4.1", "gpt-4.1-2025-04-14", "gpt-4.1-mini", "gpt-4.1-mini-2025-04-14",
110
+ "gpt-4.1-nano", "gpt-4.1-nano-2025-04-14",
111
+ "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20",
112
+ "gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01",
113
+ "gpt-4o-audio-preview-2024-12-17", "gpt-4o-audio-preview-2025-06-03",
114
+ "gpt-4o-mini", "gpt-4o-mini-2024-07-18",
115
+ "gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17",
116
+ "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17",
117
+ "gpt-4o-mini-search-preview", "gpt-4o-mini-search-preview-2025-03-11",
118
+ "gpt-4o-mini-transcribe", "gpt-4o-mini-tts",
119
+ "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01",
120
+ "gpt-4o-realtime-preview-2024-12-17", "gpt-4o-realtime-preview-2025-06-03",
121
+ "gpt-4o-search-preview", "gpt-4o-search-preview-2025-03-11",
122
+ "gpt-4o-transcribe"
123
+ ]
124
+
125
+ for model in gpt4_and_newer_models:
126
+ # Determine capabilities based on model features
127
+ vision_support = ("gpt-4o" in model or "audio-preview" in model or "realtime" in model)
128
+ is_mini_or_nano = ("mini" in model or "nano" in model)
129
+ is_audio = "audio" in model or "realtime" in model or "transcribe" in model
130
+ is_gpt41 = "gpt-4.1" in model
131
+
132
+ # Set context and output tokens based on model tier
133
+ if is_gpt41:
134
+ context_tokens = 200000 if not is_mini_or_nano else 128000
135
+ output_tokens = 32768 if not is_mini_or_nano else 16384
136
+ elif "gpt-4o" in model:
137
+ context_tokens = 128000
138
+ output_tokens = 16384
139
+ else: # GPT-4 series
140
+ context_tokens = 32000
141
+ output_tokens = 8192
142
+
143
+ self._models[model] = ModelCapabilities(
144
+ model_type=ModelType.CHAT,
145
+ supports_tools=True,
146
+ supports_streaming=not is_audio, # Audio models may not support streaming
147
+ supports_vision=vision_support,
148
+ max_context_tokens=context_tokens,
149
+ max_output_tokens=output_tokens
150
+ )
151
+
152
+ # Chat Models (GPT-3.5 series) - Updated 2025-09-28
153
+ gpt35_models = [
154
+ "gpt-3.5-turbo", "gpt-3.5-turbo-0125", "gpt-3.5-turbo-1106",
155
+ "gpt-3.5-turbo-16k", "gpt-3.5-turbo-instruct", "gpt-3.5-turbo-instruct-0914"
156
+ ]
157
+
158
+ for model in gpt35_models:
159
+ context_tokens = 16385 if "16k" not in model else 16385
160
+ self._models[model] = ModelCapabilities(
161
+ model_type=ModelType.CHAT,
162
+ supports_tools="instruct" not in model, # Instruct models don't support tools
163
+ supports_streaming="instruct" not in model, # Instruct models don't support streaming
164
+ supports_vision=False,
165
+ max_context_tokens=context_tokens,
166
+ max_output_tokens=4096
167
+ )
168
+
169
+ # Embedding Models - Updated 2025-09-28
170
+ embedding_models = [
171
+ "text-embedding-3-large", "text-embedding-3-small", "text-embedding-ada-002"
172
+ ]
173
+
174
+ for model in embedding_models:
175
+ self._models[model] = ModelCapabilities(
176
+ model_type=ModelType.EMBEDDING,
177
+ supports_tools=False,
178
+ supports_streaming=False,
179
+ supports_vision=False
180
+ )
181
+
182
+ # Pattern mappings for unknown models - Updated 2025-09-28
183
+ self._pattern_mappings = {
184
+ "o1": ModelType.REASONING,
185
+ "o3": ModelType.REASONING,
186
+ "o4": ModelType.REASONING,
187
+ "gpt-5": ModelType.REASONING, # GPT-5 is a reasoning model
188
+ "gpt-4": ModelType.CHAT,
189
+ "gpt-4.1": ModelType.CHAT,
190
+ "gpt-3.5": ModelType.CHAT,
191
+ "chatgpt": ModelType.CHAT,
192
+ "text-embedding": ModelType.EMBEDDING,
193
+ "text-moderation": ModelType.MODERATION
194
+ }
195
+
196
+ def get_model_capabilities(self, model_name: str) -> ModelCapabilities:
197
+ """
198
+ Get the capabilities for a specific model.
199
+
200
+ Parameters
201
+ ----------
202
+ model_name : str
203
+ The name of the model to look up.
204
+
205
+ Returns
206
+ -------
207
+ ModelCapabilities
208
+ The capabilities for the model.
209
+ """
210
+ # Direct lookup first
211
+ if model_name in self._models:
212
+ return self._models[model_name]
213
+
214
+ # Pattern matching for unknown models
215
+ model_lower = model_name.lower()
216
+ for pattern, model_type in self._pattern_mappings.items():
217
+ if pattern in model_lower:
218
+ logger.warning(
219
+ "Using pattern matching for unknown model",
220
+ model=model_name,
221
+ pattern=pattern,
222
+ inferred_type=model_type.value
223
+ )
224
+ # Return default capabilities for the inferred type
225
+ return self._get_default_capabilities_for_type(model_type)
226
+
227
+ # Default to chat model if no pattern matches
228
+ logger.warning(
229
+ "Unknown model, defaulting to chat model capabilities",
230
+ model=model_name
231
+ )
232
+ return self._get_default_capabilities_for_type(ModelType.CHAT)
233
+
234
+ def _get_default_capabilities_for_type(self, model_type: ModelType) -> ModelCapabilities:
235
+ """Get default capabilities for a model type."""
236
+ if model_type == ModelType.REASONING:
237
+ return ModelCapabilities(
238
+ model_type=ModelType.REASONING,
239
+ supports_tools=False,
240
+ supports_streaming=False,
241
+ supports_vision=False
242
+ )
243
+ elif model_type == ModelType.CHAT:
244
+ return ModelCapabilities(
245
+ model_type=ModelType.CHAT,
246
+ supports_tools=True,
247
+ supports_streaming=True,
248
+ supports_vision=False
249
+ )
250
+ elif model_type == ModelType.EMBEDDING:
251
+ return ModelCapabilities(
252
+ model_type=ModelType.EMBEDDING,
253
+ supports_tools=False,
254
+ supports_streaming=False,
255
+ supports_vision=False
256
+ )
257
+ else: # MODERATION
258
+ return ModelCapabilities(
259
+ model_type=ModelType.MODERATION,
260
+ supports_tools=False,
261
+ supports_streaming=False,
262
+ supports_vision=False
263
+ )
264
+
265
+ def is_reasoning_model(self, model_name: str) -> bool:
266
+ """
267
+ Check if a model is a reasoning model.
268
+
269
+ Parameters
270
+ ----------
271
+ model_name : str
272
+ The name of the model to check.
273
+
274
+ Returns
275
+ -------
276
+ bool
277
+ True if the model is a reasoning model, False otherwise.
278
+ """
279
+ capabilities = self.get_model_capabilities(model_name)
280
+ return capabilities.model_type == ModelType.REASONING
281
+
282
+ def get_registered_models(self) -> List[str]:
283
+ """
284
+ Get a list of all explicitly registered models.
285
+
286
+ Returns
287
+ -------
288
+ List[str]
289
+ List of registered model names.
290
+ """
291
+ return list(self._models.keys())
292
+
293
+ def register_model(self, model_name: str, capabilities: ModelCapabilities):
294
+ """
295
+ Register a new model with its capabilities.
296
+
297
+ Parameters
298
+ ----------
299
+ model_name : str
300
+ The name of the model to register.
301
+ capabilities : ModelCapabilities
302
+ The capabilities of the model.
303
+ """
304
+ self._models[model_name] = capabilities
305
+ logger.info("Registered new model", model=model_name, type=capabilities.model_type.value)
306
+
307
+ def register_pattern(self, pattern: str, model_type: ModelType):
308
+ """
309
+ Register a pattern for inferring model types.
310
+
311
+ Parameters
312
+ ----------
313
+ pattern : str
314
+ The pattern to match in model names.
315
+ model_type : ModelType
316
+ The type to infer for matching models.
317
+ """
318
+ self._pattern_mappings[pattern] = model_type
319
+ logger.info("Registered new pattern", pattern=pattern, type=model_type.value)
320
+
321
+
322
+ # Global registry instance
323
+ _registry = OpenAIModelRegistry()
324
+
325
+ def get_model_registry() -> OpenAIModelRegistry:
326
+ """Get the global OpenAI model registry instance."""
327
+ return _registry
@@ -0,0 +1,181 @@
1
+ """
2
+ Tests for the OpenAI Model Registry system.
3
+ """
4
+
5
+ import pytest
6
+ from mojentic.llm.gateways.openai_model_registry import (
7
+ OpenAIModelRegistry,
8
+ ModelType,
9
+ ModelCapabilities,
10
+ get_model_registry
11
+ )
12
+
13
+
14
+ class DescribeOpenAIModelRegistry:
15
+ """Specification for the OpenAI Model Registry."""
16
+
17
+ def should_initialize_with_default_models(self):
18
+ """
19
+ Given a new model registry
20
+ When initialized
21
+ Then it should contain default models for all major OpenAI model families
22
+ """
23
+ registry = OpenAIModelRegistry()
24
+ registered_models = registry.get_registered_models()
25
+
26
+ # Check that we have reasoning models
27
+ assert "o1" in registered_models
28
+ assert "o1-mini" in registered_models
29
+
30
+ # Check that we have chat models
31
+ assert "gpt-4o" in registered_models
32
+ assert "gpt-4o-mini" in registered_models
33
+ assert "gpt-3.5-turbo" in registered_models
34
+
35
+ # Check that we have embedding models
36
+ assert "text-embedding-3-large" in registered_models
37
+ assert "text-embedding-3-small" in registered_models
38
+
39
+ def should_identify_reasoning_models_correctly(self):
40
+ """
41
+ Given various model names
42
+ When checking if they are reasoning models
43
+ Then it should correctly classify known reasoning models
44
+ """
45
+ registry = OpenAIModelRegistry()
46
+
47
+ # Test known reasoning models
48
+ assert registry.is_reasoning_model("o1-preview") is True
49
+ assert registry.is_reasoning_model("o1-mini") is True
50
+ assert registry.is_reasoning_model("o3-mini") is True
51
+
52
+ # Test chat models
53
+ assert registry.is_reasoning_model("gpt-4o") is False
54
+ assert registry.is_reasoning_model("gpt-4o-mini") is False
55
+ assert registry.is_reasoning_model("gpt-3.5-turbo") is False
56
+
57
+ def should_use_pattern_matching_for_unknown_models(self):
58
+ """
59
+ Given an unknown model name that matches a pattern
60
+ When getting model capabilities
61
+ Then it should infer the correct model type
62
+ """
63
+ registry = OpenAIModelRegistry()
64
+
65
+ # Test unknown reasoning model
66
+ capabilities = registry.get_model_capabilities("o1-super-new")
67
+ assert capabilities.model_type == ModelType.REASONING
68
+ assert capabilities.get_token_limit_param() == "max_completion_tokens"
69
+
70
+ # Test unknown chat model
71
+ capabilities = registry.get_model_capabilities("gpt-4-future")
72
+ assert capabilities.model_type == ModelType.CHAT
73
+ assert capabilities.get_token_limit_param() == "max_tokens"
74
+
75
+ def should_return_correct_token_limit_parameters(self):
76
+ """
77
+ Given models of different types
78
+ When getting their token limit parameters
79
+ Then it should return the correct parameter name
80
+ """
81
+ registry = OpenAIModelRegistry()
82
+
83
+ # Reasoning models should use max_completion_tokens
84
+ o1_capabilities = registry.get_model_capabilities("o1-mini")
85
+ assert o1_capabilities.get_token_limit_param() == "max_completion_tokens"
86
+
87
+ # Chat models should use max_tokens
88
+ gpt4_capabilities = registry.get_model_capabilities("gpt-4o")
89
+ assert gpt4_capabilities.get_token_limit_param() == "max_tokens"
90
+
91
+ def should_allow_registering_new_models(self):
92
+ """
93
+ Given a new model with specific capabilities
94
+ When registering it in the registry
95
+ Then it should be available for lookup
96
+ """
97
+ registry = OpenAIModelRegistry()
98
+
99
+ new_capabilities = ModelCapabilities(
100
+ model_type=ModelType.REASONING,
101
+ supports_tools=True,
102
+ supports_streaming=True,
103
+ max_output_tokens=50000
104
+ )
105
+
106
+ registry.register_model("o5-preview", new_capabilities)
107
+
108
+ retrieved_capabilities = registry.get_model_capabilities("o5-preview")
109
+ assert retrieved_capabilities.model_type == ModelType.REASONING
110
+ assert retrieved_capabilities.supports_tools is True
111
+ assert retrieved_capabilities.max_output_tokens == 50000
112
+
113
+ def should_allow_registering_new_patterns(self):
114
+ """
115
+ Given a new pattern for model type inference
116
+ When registering it in the registry
117
+ Then it should be used for unknown models matching the pattern
118
+ """
119
+ registry = OpenAIModelRegistry()
120
+
121
+ registry.register_pattern("claude", ModelType.CHAT)
122
+
123
+ capabilities = registry.get_model_capabilities("claude-3-opus")
124
+ assert capabilities.model_type == ModelType.CHAT
125
+
126
+ def should_handle_completely_unknown_models(self):
127
+ """
128
+ Given a completely unknown model name with no matching patterns
129
+ When getting model capabilities
130
+ Then it should default to chat model capabilities
131
+ """
132
+ registry = OpenAIModelRegistry()
133
+
134
+ capabilities = registry.get_model_capabilities("completely-unknown-model-xyz")
135
+ assert capabilities.model_type == ModelType.CHAT
136
+ assert capabilities.get_token_limit_param() == "max_tokens"
137
+
138
+ def should_provide_global_registry_instance(self):
139
+ """
140
+ Given the global registry function
141
+ When called multiple times
142
+ Then it should return the same instance
143
+ """
144
+ registry1 = get_model_registry()
145
+ registry2 = get_model_registry()
146
+
147
+ assert registry1 is registry2
148
+
149
+ def should_handle_model_capabilities_dataclass_correctly(self):
150
+ """
151
+ Given model capabilities
152
+ When created with different parameters
153
+ Then it should handle defaults and customizations correctly
154
+ """
155
+ # Test with defaults
156
+ default_caps = ModelCapabilities(model_type=ModelType.CHAT)
157
+ assert default_caps.supports_tools is True
158
+ assert default_caps.supports_streaming is True
159
+ assert default_caps.supports_vision is False
160
+
161
+ # Test with custom values
162
+ custom_caps = ModelCapabilities(
163
+ model_type=ModelType.REASONING,
164
+ supports_tools=False,
165
+ supports_vision=True,
166
+ max_context_tokens=100000
167
+ )
168
+ assert custom_caps.supports_tools is False
169
+ assert custom_caps.supports_vision is True
170
+ assert custom_caps.max_context_tokens == 100000
171
+
172
+ def should_have_correct_model_type_enum_values(self):
173
+ """
174
+ Given the ModelType enum
175
+ When accessing its values
176
+ Then it should have all expected model types
177
+ """
178
+ assert ModelType.REASONING.value == "reasoning"
179
+ assert ModelType.CHAT.value == "chat"
180
+ assert ModelType.EMBEDDING.value == "embedding"
181
+ assert ModelType.MODERATION.value == "moderation"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mojentic
3
- Version: 0.7.4
3
+ Version: 0.8.0
4
4
  Summary: Mojentic is an agentic framework that aims to provide a simple and flexible way to assemble teams of agents to solve complex problems.
5
5
  Author-email: Stacey Vetzal <stacey@vetzal.com>
6
6
  Project-URL: Homepage, https://github.com/svetzal/mojentic
@@ -39,7 +39,7 @@ Dynamic: license-file
39
39
 
40
40
  # Mojentic
41
41
 
42
- Mojentic is a framework that provides a simple and flexible way to interact with Large Language Models (LLMs). It offers integration with various LLM providers and includes tools for structured output generation, task automation, and more. The future direction is to facilitate a team of agents, but the current focus is on robust LLM interaction capabilities.
42
+ Mojentic is a framework that provides a simple and flexible way to interact with Large Language Models (LLMs). It offers integration with various LLM providers and includes tools for structured output generation, task automation, and more. With comprehensive support for all OpenAI models including GPT-5 and automatic parameter adaptation, Mojentic handles the complexities of different model types seamlessly. The future direction is to facilitate a team of agents, but the current focus is on robust LLM interaction capabilities.
43
43
 
44
44
  [![GitHub](https://img.shields.io/github/license/svetzal/mojentic)](LICENSE.md)
45
45
  [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
@@ -48,6 +48,8 @@ Mojentic is a framework that provides a simple and flexible way to interact with
48
48
  ## 🚀 Features
49
49
 
50
50
  - **LLM Integration**: Support for multiple LLM providers (OpenAI, Ollama)
51
+ - **Latest OpenAI Models**: Full support for GPT-5, GPT-4.1, and all reasoning models (o1, o3, o4 series)
52
+ - **Automatic Model Adaptation**: Seamless parameter handling across different OpenAI model types
51
53
  - **Structured Output**: Generate structured data from LLM responses using Pydantic models
52
54
  - **Tools Integration**: Utilities for date resolution, image analysis, and more
53
55
  - **Multi-modal Capabilities**: Process and analyze images alongside text
@@ -84,8 +86,9 @@ from mojentic.llm.gateways.models import LLMMessage
84
86
  from mojentic.llm.tools.date_resolver import ResolveDateTool
85
87
  from pydantic import BaseModel, Field
86
88
 
87
- # Initialize with OpenAI
88
- openai_llm = LLMBroker(model="gpt-4o", gateway=OpenAIGateway(api_key="your_api_key"))
89
+ # Initialize with OpenAI (supports all models including GPT-5, GPT-4.1, reasoning models)
90
+ openai_llm = LLMBroker(model="gpt-5", gateway=OpenAIGateway(api_key="your_api_key"))
91
+ # Or use other models: "gpt-4o", "gpt-4.1", "o1-mini", "o3-mini", etc.
89
92
 
90
93
  # Or use Ollama for local LLMs
91
94
  ollama_llm = LLMBroker(model="llama3")
@@ -99,7 +102,7 @@ class Sentiment(BaseModel):
99
102
  label: str = Field(..., description="Label for the sentiment")
100
103
 
101
104
  sentiment = openai_llm.generate_object(
102
- messages=[LLMMessage(content="Hello, how are you?")],
105
+ messages=[LLMMessage(content="Hello, how are you?")],
103
106
  object_model=Sentiment
104
107
  )
105
108
  print(sentiment.label)
@@ -118,6 +121,10 @@ result = openai_llm.generate(messages=[
118
121
  print(result)
119
122
  ```
120
123
 
124
+ ## 🤖 OpenAI Model Support
125
+
126
+ The framework automatically handles parameter differences between model types, so you can switch between any models without code changes.
127
+
121
128
  ## 🏗️ Project Structure
122
129
 
123
130
  ```
@@ -2,7 +2,7 @@ _examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  _examples/async_dispatcher_example.py,sha256=rlaShGpXUsTN7RwVxOo8sbxQjYYJ13gKRB9PWQ3sVTE,8454
3
3
  _examples/async_llm_example.py,sha256=IgYBMawHhaKWbA6zkurH-raqBez9nODfjHEAHI7PRek,8708
4
4
  _examples/broker_as_tool.py,sha256=QJHryYqIXSUYkK62LhMspbNdK3yL0DoenWHxUK5c_fk,2971
5
- _examples/broker_examples.py,sha256=7tES2KyKc3uigBuxkwOTf5UGhp7nVeVqLhd8fEHSWEc,1901
5
+ _examples/broker_examples.py,sha256=-fNqOLl4lwja3_sQwVJyT-2Qwy-B0ol9UWs6zSP2QKk,2397
6
6
  _examples/broker_image_examples.py,sha256=8dpZ2RFmRYhJqoeWklzlPxmvj9m4M393452SO_D2bJY,1133
7
7
  _examples/characterize_ollama.py,sha256=_TzPEAoTuB-saTiaypEQAsUpiBeM37l9I5wCMU3GM4E,2521
8
8
  _examples/characterize_openai.py,sha256=JQQNjsHIEegjLAS3uzDmq_Ci_ZqXTqjcoVaC-rS_R_8,564
@@ -14,6 +14,7 @@ _examples/design_analysis.py,sha256=66JxvnA065QV__WXpxJX_eTBDan34nGcKvao75UPLlw,
14
14
  _examples/embeddings.py,sha256=94DAMsMB35BU98hOfOeOsbqntcequFSdtaStox2hTvk,267
15
15
  _examples/ensures_files_exist.py,sha256=LAwX9YtUZGI6NT254PY7oC5yg6q-b9Q_Dez9gGrabQM,2009
16
16
  _examples/ephemeral_task_manager_example.py,sha256=Q1YpLPwDkB-ytIGgCh2eWOr5EJ0FE-osxJ0B0wDlQkY,1491
17
+ _examples/fetch_openai_models.py,sha256=06FrBZfV4FJ7MM9MczbrzFD8wUmXKZOjzPFFxwSVlj4,3545
17
18
  _examples/file_deduplication.py,sha256=jYayx4BCrk1uJwVBkjIXzguLQOWLREi4PBPzoK9LuOU,1665
18
19
  _examples/file_tool.py,sha256=fUzaIF8qqIy3-E6TJ89jOSD2dwjuO_gBi_1vfCzjuC4,2335
19
20
  _examples/image_analysis.py,sha256=Kj49vLQD1DIpvv5P7rir4BqzsVCTgq-tfppqXcYVJkA,503
@@ -22,6 +23,7 @@ _examples/image_broker_splat.py,sha256=O7rzTFUka32if4G4VuXvhu1O-2lRMWfi0r8gjIE8-
22
23
  _examples/iterative_solver.py,sha256=ANGdC74ymHosVt6xUBjplkJl_W3ALTGxOkDpPLEDcm8,1331
23
24
  _examples/list_models.py,sha256=8noMpGeXOdX5Pf0NXCt_CRurOKEg_5luhWveGntBhe8,578
24
25
  _examples/model_characterization.py,sha256=XwLiUP1ZIrNs4ZLmjLDW-nJQsB66H-BV0bWgBgT3N7k,2571
26
+ _examples/openai_gateway_enhanced_demo.py,sha256=vNgJSeQS78PSUibFR9I3WSrBEoSW5qr0dkf2UNlZRdI,5995
25
27
  _examples/oversized_embeddings.py,sha256=_z2JoqZn0g7VtRsFVWIkngVqzjhQQvCEUYWVxs1I7MM,284
26
28
  _examples/raw.py,sha256=Y2wvgynFuoUs28agE4ijsLYec8VRjiReklqlCH2lERs,442
27
29
  _examples/react.py,sha256=VQ-5MmjUXoHzBFPTV_JrocuOkDzZ8oyUUSYLlEToJ_0,939
@@ -84,9 +86,11 @@ mojentic/llm/gateways/models.py,sha256=OyIaMHKrrx6dHo5FbC8qOFct7PRql9wqbe_BJlgDS
84
86
  mojentic/llm/gateways/ollama.py,sha256=OUUImBNzPte52Gsf-e7TBjDHRvYW5flU9ddxwG2zlzk,7909
85
87
  mojentic/llm/gateways/ollama_messages_adapter.py,sha256=kUN_p2FyN88_trXMcL-Xsn9xPBU7pGKlJwTUEUCf6G4,1404
86
88
  mojentic/llm/gateways/ollama_messages_adapter_spec.py,sha256=gVRbWDrHOa1EiZ0CkEWe0pGn-GKRqdGb-x56HBQeYSE,4981
87
- mojentic/llm/gateways/openai.py,sha256=Wxx_WfG2czOv9Ng8q4JCLFIHGqNs3vaMtE5ggSLBjHk,7787
89
+ mojentic/llm/gateways/openai.py,sha256=42A-8etuDRBSy18q5Qp6S1yndyOJpu3p8Pu0Dd1orFU,12806
88
90
  mojentic/llm/gateways/openai_message_adapter_spec.py,sha256=ITBSV5njldV_x0NPgjmg8Okf9KzevQJ8dTXM-t6ubcg,6612
89
91
  mojentic/llm/gateways/openai_messages_adapter.py,sha256=Scal68JKKdBHB35ok1c5DeWYdD6Wra5oXSsPxJyyXSQ,3947
92
+ mojentic/llm/gateways/openai_model_registry.py,sha256=4BIWQOl-5yAbug3UHUtpbj3kpkadNoy4sMgThyPi-i8,12858
93
+ mojentic/llm/gateways/openai_model_registry_spec.py,sha256=rCyXhiCOKMewkZjdZoawALoEk62yjENeYTpjYuMuXDM,6711
90
94
  mojentic/llm/gateways/tokenizer_gateway.py,sha256=ztuqfunlJ6xmyUPPHcC_69-kegiNJD6jdSEde7hDh2w,485
91
95
  mojentic/llm/registry/__init__.py,sha256=P2MHlptrtRPMSWbWl9ojXPmjMwkW0rIn6jwzCkSgnhE,164
92
96
  mojentic/llm/registry/llm_registry.py,sha256=beyrgGrkXx5ZckUJzC1nQ461vra0fF6s_qRaEdi5bsg,2508
@@ -132,8 +136,8 @@ mojentic/tracer/tracer_system.py,sha256=7CPy_2tlsHtXQ4DcO5oo52N9a9WS0GH-mjeINzu6
132
136
  mojentic/tracer/tracer_system_spec.py,sha256=TNm0f9LV__coBx0JGEKyzzNN9mFjCSG_SSrRISO8Xeg,8632
133
137
  mojentic/utils/__init__.py,sha256=lqECkkoFvHFttDnafRE1vvh0Dmna_lwupMToP5VvX5k,115
134
138
  mojentic/utils/formatting.py,sha256=bPrwwdluXdQ8TsFxfWtHNOeMWKNvAfABSoUnnA1g7c8,947
135
- mojentic-0.7.4.dist-info/licenses/LICENSE.md,sha256=txSgV8n5zY1W3NiF5HHsCwlaW0e8We1cSC6TuJUqxXA,1060
136
- mojentic-0.7.4.dist-info/METADATA,sha256=shFHb00Ezpkzo6QJOp1vAIhXGONL2jFH7pzIJimPBao,5475
137
- mojentic-0.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
138
- mojentic-0.7.4.dist-info/top_level.txt,sha256=Q-BvPQ8Eu1jnEqK8Xkr6A9C8Xa1z38oPZRHuA5MCTqg,19
139
- mojentic-0.7.4.dist-info/RECORD,,
139
+ mojentic-0.8.0.dist-info/licenses/LICENSE.md,sha256=txSgV8n5zY1W3NiF5HHsCwlaW0e8We1cSC6TuJUqxXA,1060
140
+ mojentic-0.8.0.dist-info/METADATA,sha256=EpjGKSzORxFrSG94rw5rUvm4vztoEMfGCTkXON-W49k,6154
141
+ mojentic-0.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
142
+ mojentic-0.8.0.dist-info/top_level.txt,sha256=Q-BvPQ8Eu1jnEqK8Xkr6A9C8Xa1z38oPZRHuA5MCTqg,19
143
+ mojentic-0.8.0.dist-info/RECORD,,