hammad-python 0.0.21__py3-none-any.whl → 0.0.23__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.
@@ -354,22 +354,43 @@ class Agent(BaseGenAIModel, Generic[T]):
354
354
  ) -> dict:
355
355
  """Get effective context settings, using provided parameters or defaults."""
356
356
  return {
357
- "context_updates": context_updates if context_updates is not None else self.context_updates,
358
- "context_confirm": context_confirm if context_confirm is not None else self.context_confirm,
359
- "context_strategy": context_strategy if context_strategy is not None else self.context_strategy,
360
- "context_max_retries": context_max_retries if context_max_retries is not None else self.context_max_retries,
361
- "context_confirm_instructions": context_confirm_instructions if context_confirm_instructions is not None else self.context_confirm_instructions,
362
- "context_selection_instructions": context_selection_instructions if context_selection_instructions is not None else self.context_selection_instructions,
363
- "context_update_instructions": context_update_instructions if context_update_instructions is not None else self.context_update_instructions,
364
- "context_format": context_format if context_format is not None else self.context_format,
357
+ "context_updates": context_updates
358
+ if context_updates is not None
359
+ else self.context_updates,
360
+ "context_confirm": context_confirm
361
+ if context_confirm is not None
362
+ else self.context_confirm,
363
+ "context_strategy": context_strategy
364
+ if context_strategy is not None
365
+ else self.context_strategy,
366
+ "context_max_retries": context_max_retries
367
+ if context_max_retries is not None
368
+ else self.context_max_retries,
369
+ "context_confirm_instructions": context_confirm_instructions
370
+ if context_confirm_instructions is not None
371
+ else self.context_confirm_instructions,
372
+ "context_selection_instructions": context_selection_instructions
373
+ if context_selection_instructions is not None
374
+ else self.context_selection_instructions,
375
+ "context_update_instructions": context_update_instructions
376
+ if context_update_instructions is not None
377
+ else self.context_update_instructions,
378
+ "context_format": context_format
379
+ if context_format is not None
380
+ else self.context_format,
365
381
  }
366
382
 
367
383
  def _should_update_context(
368
- self, context: AgentContext, timing: Literal["before", "after"], context_updates=None
384
+ self,
385
+ context: AgentContext,
386
+ timing: Literal["before", "after"],
387
+ context_updates=None,
369
388
  ) -> bool:
370
389
  """Determine if context should be updated based on timing and configuration."""
371
- effective_context_updates = context_updates if context_updates is not None else self.context_updates
372
-
390
+ effective_context_updates = (
391
+ context_updates if context_updates is not None else self.context_updates
392
+ )
393
+
373
394
  if not effective_context_updates:
374
395
  return False
375
396
 
@@ -405,7 +426,9 @@ class Agent(BaseGenAIModel, Generic[T]):
405
426
  if isinstance(context, BaseModel):
406
427
  field_type = context.__class__.model_fields[field_name].annotation
407
428
  field_info = context.__class__.model_fields[field_name]
408
- description = getattr(field_info, 'description', f"Update the {field_name} field")
429
+ description = getattr(
430
+ field_info, "description", f"Update the {field_name} field"
431
+ )
409
432
  elif isinstance(context, dict):
410
433
  field_type = type(context[field_name])
411
434
  description = f"Update the {field_name} field"
@@ -415,7 +438,7 @@ class Agent(BaseGenAIModel, Generic[T]):
415
438
 
416
439
  return create_model(
417
440
  f"Update{field_name.capitalize()}",
418
- **{field_name: (field_type, Field(description=description))}
441
+ **{field_name: (field_type, Field(description=description))},
419
442
  )
420
443
  else:
421
444
  # All fields update - create a model with the exact same fields as the context
@@ -425,9 +448,14 @@ class Agent(BaseGenAIModel, Generic[T]):
425
448
  for field_name, field_info in context.model_fields.items():
426
449
  field_type = field_info.annotation
427
450
  current_value = getattr(context, field_name)
428
- description = getattr(field_info, 'description', f"Current value: {current_value}")
429
- field_definitions[field_name] = (field_type, Field(description=description))
430
-
451
+ description = getattr(
452
+ field_info, "description", f"Current value: {current_value}"
453
+ )
454
+ field_definitions[field_name] = (
455
+ field_type,
456
+ Field(description=description),
457
+ )
458
+
431
459
  return create_model("ContextUpdate", **field_definitions)
432
460
  elif isinstance(context, dict):
433
461
  # Create a model with the same keys as the dict
@@ -435,12 +463,21 @@ class Agent(BaseGenAIModel, Generic[T]):
435
463
  for key, value in context.items():
436
464
  field_type = type(value)
437
465
  description = f"Current value: {value}"
438
- field_definitions[key] = (field_type, Field(description=description))
439
-
466
+ field_definitions[key] = (
467
+ field_type,
468
+ Field(description=description),
469
+ )
470
+
440
471
  return create_model("ContextUpdate", **field_definitions)
441
472
  else:
442
473
  # Fallback to generic updates
443
- return create_model("ContextUpdate", updates=(Dict[str, Any], Field(description="Dictionary of field updates")))
474
+ return create_model(
475
+ "ContextUpdate",
476
+ updates=(
477
+ Dict[str, Any],
478
+ Field(description="Dictionary of field updates"),
479
+ ),
480
+ )
444
481
 
445
482
  def _perform_context_update(
446
483
  self,
@@ -452,7 +489,7 @@ class Agent(BaseGenAIModel, Generic[T]):
452
489
  ) -> AgentContext:
453
490
  """Perform context update with retries and error handling."""
454
491
  updated_context = context
455
-
492
+
456
493
  # Use effective settings or defaults
457
494
  if effective_settings is None:
458
495
  effective_settings = {
@@ -470,16 +507,18 @@ class Agent(BaseGenAIModel, Generic[T]):
470
507
  # Check if update is needed (if confirmation is enabled)
471
508
  if effective_settings["context_confirm"]:
472
509
  confirm_model = self._create_context_confirm_model()
473
-
510
+
474
511
  # Create detailed instructions with context structure
475
- context_structure = _format_context_for_instructions(updated_context, effective_settings["context_format"])
512
+ context_structure = _format_context_for_instructions(
513
+ updated_context, effective_settings["context_format"]
514
+ )
476
515
  confirm_instructions = f"""Based on the conversation, determine if the context should be updated {timing} processing.
477
516
 
478
517
  Current context structure:
479
518
  {context_structure}
480
519
 
481
520
  Should the context be updated based on the new information provided in the conversation?"""
482
-
521
+
483
522
  if effective_settings["context_confirm_instructions"]:
484
523
  confirm_instructions += f"\n\nAdditional instructions: {effective_settings['context_confirm_instructions']}"
485
524
 
@@ -499,16 +538,18 @@ Should the context be updated based on the new information provided in the conve
499
538
  selection_model = self._create_context_selection_model(
500
539
  updated_context
501
540
  )
502
-
541
+
503
542
  # Create detailed instructions with context structure
504
- context_structure = _format_context_for_instructions(updated_context, effective_settings["context_format"])
543
+ context_structure = _format_context_for_instructions(
544
+ updated_context, effective_settings["context_format"]
545
+ )
505
546
  selection_instructions = f"""Select which fields in the context should be updated {timing} processing based on the conversation.
506
547
 
507
548
  Current context structure:
508
549
  {context_structure}
509
550
 
510
551
  Choose only the fields that need to be updated based on the new information provided in the conversation."""
511
-
552
+
512
553
  if effective_settings["context_selection_instructions"]:
513
554
  selection_instructions += f"\n\nAdditional instructions: {effective_settings['context_selection_instructions']}"
514
555
 
@@ -526,14 +567,18 @@ Choose only the fields that need to be updated based on the new information prov
526
567
  updated_context, field_name
527
568
  )
528
569
  # Get current field value for context
529
- current_value = getattr(updated_context, field_name) if isinstance(updated_context, BaseModel) else updated_context.get(field_name)
530
-
570
+ current_value = (
571
+ getattr(updated_context, field_name)
572
+ if isinstance(updated_context, BaseModel)
573
+ else updated_context.get(field_name)
574
+ )
575
+
531
576
  field_instructions = f"""Update the {field_name} field in the context based on the conversation.
532
577
 
533
578
  Current value of {field_name}: {current_value}
534
579
 
535
580
  Please provide the new value for {field_name} based on the information from the conversation."""
536
-
581
+
537
582
  if effective_settings["context_update_instructions"]:
538
583
  field_instructions += f"\n\nAdditional instructions: {effective_settings['context_update_instructions']}"
539
584
 
@@ -555,16 +600,18 @@ Please provide the new value for {field_name} based on the information from the
555
600
  else: # strategy == "all"
556
601
  # Update all fields at once
557
602
  update_model = self._create_context_update_model(updated_context)
558
-
603
+
559
604
  # Create detailed instructions with context structure
560
- context_structure = _format_context_for_instructions(updated_context, effective_settings["context_format"])
605
+ context_structure = _format_context_for_instructions(
606
+ updated_context, effective_settings["context_format"]
607
+ )
561
608
  update_instructions = f"""Update the context {timing} processing based on the conversation.
562
609
 
563
610
  Current context structure:
564
611
  {context_structure}
565
612
 
566
613
  Please update the appropriate fields based on the conversation. Only update fields that need to be changed based on the new information provided."""
567
-
614
+
568
615
  if effective_settings["context_update_instructions"]:
569
616
  update_instructions += f"\n\nAdditional instructions: {effective_settings['context_update_instructions']}"
570
617
 
@@ -576,7 +623,7 @@ Please update the appropriate fields based on the conversation. Only update fiel
576
623
  )
577
624
 
578
625
  # Apply the updates
579
- if hasattr(update_response.output, 'updates'):
626
+ if hasattr(update_response.output, "updates"):
580
627
  # Legacy fallback for generic updates
581
628
  updated_context = _update_context_object(
582
629
  updated_context, update_response.output.updates
@@ -584,10 +631,18 @@ Please update the appropriate fields based on the conversation. Only update fiel
584
631
  else:
585
632
  # New approach - extract field values directly from the response
586
633
  updates_dict = {}
587
- for field_name in (context.model_fields.keys() if isinstance(context, BaseModel) else context.keys()):
634
+ for field_name in (
635
+ context.model_fields.keys()
636
+ if isinstance(context, BaseModel)
637
+ else context.keys()
638
+ ):
588
639
  if hasattr(update_response.output, field_name):
589
- updates_dict[field_name] = getattr(update_response.output, field_name)
590
- updated_context = _update_context_object(updated_context, updates_dict)
640
+ updates_dict[field_name] = getattr(
641
+ update_response.output, field_name
642
+ )
643
+ updated_context = _update_context_object(
644
+ updated_context, updates_dict
645
+ )
591
646
 
592
647
  # Trigger context update hooks
593
648
  self.hook_manager.trigger_hooks("context_update", updated_context)
@@ -808,7 +863,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
808
863
  # RUN MAIN AGENTIC LOOP
809
864
  for step in range(max_steps):
810
865
  # Update context before processing if configured
811
- if context and self._should_update_context(context, "before", effective_context_settings["context_updates"]):
866
+ if context and self._should_update_context(
867
+ context, "before", effective_context_settings["context_updates"]
868
+ ):
812
869
  context = self._perform_context_update(
813
870
  context=context,
814
871
  model=working_model,
@@ -836,9 +893,7 @@ Please update the appropriate fields based on the conversation. Only update fiel
836
893
  # Get language model response
837
894
  response = working_model.run(
838
895
  messages=formatted_messages,
839
- tools=[tool.to_dict() for tool in self.tools]
840
- if self.tools
841
- else None,
896
+ tools=[tool.to_dict() for tool in self.tools] if self.tools else None,
842
897
  **model_kwargs,
843
898
  )
844
899
 
@@ -860,7 +915,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
860
915
  else:
861
916
  # No tool calls - this is the final step
862
917
  # Update context after processing if configured
863
- if context and self._should_update_context(context, "after", effective_context_settings["context_updates"]):
918
+ if context and self._should_update_context(
919
+ context, "after", effective_context_settings["context_updates"]
920
+ ):
864
921
  context = self._perform_context_update(
865
922
  context=context,
866
923
  model=working_model,
@@ -886,7 +943,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
886
943
  )
887
944
 
888
945
  # Update context after processing if configured
889
- if context and self._should_update_context(context, "after", effective_context_settings["context_updates"]):
946
+ if context and self._should_update_context(
947
+ context, "after", effective_context_settings["context_updates"]
948
+ ):
890
949
  context = self._perform_context_update(
891
950
  context=context,
892
951
  model=working_model,
@@ -1017,7 +1076,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
1017
1076
  # RUN MAIN AGENTIC LOOP
1018
1077
  for step in range(max_steps):
1019
1078
  # Update context before processing if configured
1020
- if context and self._should_update_context(context, "before", effective_context_settings["context_updates"]):
1079
+ if context and self._should_update_context(
1080
+ context, "before", effective_context_settings["context_updates"]
1081
+ ):
1021
1082
  context = self._perform_context_update(
1022
1083
  context=context,
1023
1084
  model=working_model,
@@ -1045,9 +1106,7 @@ Please update the appropriate fields based on the conversation. Only update fiel
1045
1106
  # Get language model response
1046
1107
  response = await working_model.async_run(
1047
1108
  messages=formatted_messages,
1048
- tools=[tool.to_dict() for tool in self.tools]
1049
- if self.tools
1050
- else None,
1109
+ tools=[tool.to_dict() for tool in self.tools] if self.tools else None,
1051
1110
  **model_kwargs,
1052
1111
  )
1053
1112
 
@@ -1069,7 +1128,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
1069
1128
  else:
1070
1129
  # No tool calls - this is the final step
1071
1130
  # Update context after processing if configured
1072
- if context and self._should_update_context(context, "after", effective_context_settings["context_updates"]):
1131
+ if context and self._should_update_context(
1132
+ context, "after", effective_context_settings["context_updates"]
1133
+ ):
1073
1134
  context = self._perform_context_update(
1074
1135
  context=context,
1075
1136
  model=working_model,
@@ -1095,7 +1156,9 @@ Please update the appropriate fields based on the conversation. Only update fiel
1095
1156
  )
1096
1157
 
1097
1158
  # Update context after processing if configured
1098
- if context and self._should_update_context(context, "after", effective_context_settings["context_updates"]):
1159
+ if context and self._should_update_context(
1160
+ context, "after", effective_context_settings["context_updates"]
1161
+ ):
1099
1162
  context = self._perform_context_update(
1100
1163
  context=context,
1101
1164
  model=working_model,
@@ -1288,6 +1351,84 @@ Please update the appropriate fields based on the conversation. Only update fiel
1288
1351
  )
1289
1352
 
1290
1353
 
1354
+ def create_agent(
1355
+ name: str = "agent",
1356
+ instructions: Optional[str] = None,
1357
+ model: Union[LanguageModel, LanguageModelName] = "openai/gpt-4o-mini",
1358
+ description: Optional[str] = None,
1359
+ tools: Union[List[Tool], Callable, None] = None,
1360
+ settings: Optional[AgentSettings] = None,
1361
+ instructor_mode: Optional[LanguageModelInstructorMode] = None,
1362
+ # Context management parameters
1363
+ context_updates: Optional[
1364
+ Union[List[Literal["before", "after"]], Literal["before", "after"]]
1365
+ ] = None,
1366
+ context_confirm: bool = False,
1367
+ context_strategy: Literal["selective", "all"] = "all",
1368
+ context_max_retries: int = 3,
1369
+ context_confirm_instructions: Optional[str] = None,
1370
+ context_selection_instructions: Optional[str] = None,
1371
+ context_update_instructions: Optional[str] = None,
1372
+ context_format: Literal["json", "python", "markdown"] = "json",
1373
+ **kwargs: Any,
1374
+ ) -> Agent[T]:
1375
+ """Create a new AI agent with specified capabilities and behavior.
1376
+
1377
+ An agent is an intelligent assistant that can use tools, follow instructions,
1378
+ and maintain context across conversations. It combines a language model with
1379
+ additional capabilities like tool execution and structured output generation.
1380
+
1381
+ Args:
1382
+ name: A human-readable name for the agent (default: "agent")
1383
+ instructions: System instructions that define the agent's behavior and personality
1384
+ model: The language model to use - either a LanguageModel instance or model name string
1385
+ description: Optional description of what the agent does
1386
+ tools: List of tools/functions the agent can call, or a single callable
1387
+ settings: AgentSettings object to customize default behavior
1388
+ instructor_mode: Mode for structured output generation
1389
+ context_updates: When to update context - "before", "after", or both
1390
+ context_confirm: Whether to confirm context updates with the user
1391
+ context_strategy: How to select context updates - "selective" or "all"
1392
+ context_max_retries: Maximum attempts for context update operations
1393
+ context_confirm_instructions: Custom instructions for context confirmation
1394
+ context_selection_instructions: Custom instructions for context selection
1395
+ context_update_instructions: Custom instructions for context updates
1396
+ context_format: Format for context display - "json", "python", or "markdown"
1397
+ **kwargs: Additional parameters passed to the underlying language model
1398
+
1399
+ Example:
1400
+ Basic agent:
1401
+ >>> agent = create_agent(name="assistant", instructions="You are helpful")
1402
+
1403
+ Agent with tools:
1404
+ >>> def calculator(x: int, y: int) -> int:
1405
+ ... return x + y
1406
+ >>> agent = create_agent(tools=[calculator])
1407
+
1408
+ Agent with custom settings:
1409
+ >>> settings = AgentSettings(max_steps=5)
1410
+ >>> agent = create_agent(settings=settings, model="gpt-4")
1411
+ """
1412
+ return Agent(
1413
+ name=name,
1414
+ instructions=instructions,
1415
+ model=model,
1416
+ description=description,
1417
+ tools=tools,
1418
+ settings=settings,
1419
+ instructor_mode=instructor_mode,
1420
+ context_updates=context_updates,
1421
+ context_confirm=context_confirm,
1422
+ context_strategy=context_strategy,
1423
+ context_max_retries=context_max_retries,
1424
+ context_confirm_instructions=context_confirm_instructions,
1425
+ context_selection_instructions=context_selection_instructions,
1426
+ context_update_instructions=context_update_instructions,
1427
+ context_format=context_format,
1428
+ **kwargs,
1429
+ )
1430
+
1431
+
1291
1432
  __all__ = [
1292
1433
  "Agent",
1293
1434
  "AgentSettings",
@@ -1295,4 +1436,5 @@ __all__ = [
1295
1436
  "AgentEvent",
1296
1437
  "HookManager",
1297
1438
  "HookDecorator",
1439
+ "create_agent",
1298
1440
  ]
@@ -57,7 +57,7 @@ class AgentResponse(LanguageModelResponse[T], Generic[T, AgentContext]):
57
57
  empty.
58
58
  """
59
59
 
60
- context: AgentContext = None
60
+ context: AgentContext | None = None
61
61
  """
62
62
  The final context object after agent execution.
63
63
 
@@ -68,7 +68,7 @@ class AgentResponse(LanguageModelResponse[T], Generic[T, AgentContext]):
68
68
  @cached
69
69
  def __str__(self) -> str:
70
70
  """Pretty prints the response object."""
71
- output = "AgentResponse:"
71
+ output = ">>> AgentResponse:"
72
72
 
73
73
  if self.output or self.content:
74
74
  output += f"\n{self.output if self.output else self.content}"
@@ -91,7 +91,9 @@ class AgentResponse(LanguageModelResponse[T], Generic[T, AgentContext]):
91
91
 
92
92
  # Show context if available
93
93
  if self.context:
94
- output += f"\n>>> Final Context: {self._format_context_display(self.context)}"
94
+ output += (
95
+ f"\n>>> Final Context: {self._format_context_display(self.context)}"
96
+ )
95
97
 
96
98
  return output
97
99
 
@@ -99,16 +101,16 @@ class AgentResponse(LanguageModelResponse[T], Generic[T, AgentContext]):
99
101
  """Format context for display in string representation."""
100
102
  if context is None:
101
103
  return "None"
102
-
104
+
103
105
  try:
104
106
  # For Pydantic models, show as dict
105
- if hasattr(context, 'model_dump'):
107
+ if hasattr(context, "model_dump"):
106
108
  context_dict = context.model_dump()
107
109
  elif isinstance(context, dict):
108
110
  context_dict = context
109
111
  else:
110
112
  return str(context)
111
-
113
+
112
114
  # Format as compact JSON-like string
113
115
  items = []
114
116
  for key, value in context_dict.items():
@@ -116,7 +118,7 @@ class AgentResponse(LanguageModelResponse[T], Generic[T, AgentContext]):
116
118
  items.append(f"{key}='{value}'")
117
119
  else:
118
120
  items.append(f"{key}={value}")
119
-
121
+
120
122
  return "{" + ", ".join(items) + "}"
121
123
  except Exception:
122
124
  return str(context)
@@ -87,18 +87,20 @@ class AgentResponseChunk(LanguageModelResponseChunk[T], Generic[T]):
87
87
  def __str__(self) -> str:
88
88
  """String representation of the chunk."""
89
89
  output = f"AgentResponseChunk(step={self.step_number}, final={self.is_final})"
90
-
90
+
91
91
  # Show content if available
92
92
  if self.output or self.content:
93
93
  content_preview = str(self.output if self.output else self.content)
94
94
  if len(content_preview) > 100:
95
95
  content_preview = content_preview[:100] + "..."
96
96
  output += f"\nContent: {content_preview}"
97
-
97
+
98
98
  return output
99
99
 
100
100
 
101
- class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentContext]):
101
+ class AgentStream(
102
+ BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentContext]
103
+ ):
102
104
  """Stream of agent responses that can be used in sync and async contexts."""
103
105
 
104
106
  def __init__(
@@ -151,16 +153,17 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
151
153
  def _format_messages(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
152
154
  if self.agent.instructions:
153
155
  system_content = self.agent.instructions
154
-
156
+
155
157
  # Add context if available
156
158
  if self.current_context is not None:
157
159
  from ..agent import _format_context_for_instructions
160
+
158
161
  context_str = _format_context_for_instructions(
159
162
  self.current_context, self.agent.context_format
160
163
  )
161
164
  if context_str:
162
165
  system_content += f"\n\nContext:\n{context_str}"
163
-
166
+
164
167
  system_message = {"role": "system", "content": system_content}
165
168
  messages = [system_message] + messages
166
169
  return consolidate_system_messages(messages)
@@ -184,16 +187,18 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
184
187
  else:
185
188
  self.is_done = True
186
189
  self._final_response = response
187
-
190
+
188
191
  # Update context after processing if configured
189
- if self.current_context and self.agent._should_update_context(self.current_context, "after"):
192
+ if self.current_context and self.agent._should_update_context(
193
+ self.current_context, "after"
194
+ ):
190
195
  self.current_context = self.agent._perform_context_update(
191
196
  context=self.current_context,
192
197
  model=self.model,
193
198
  current_messages=self.current_messages,
194
199
  timing="after",
195
200
  )
196
-
201
+
197
202
  return AgentResponseChunk(
198
203
  step_number=self.current_step, response=response, is_final=True
199
204
  )
@@ -204,7 +209,9 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
204
209
  self.current_step += 1
205
210
 
206
211
  # Update context before processing if configured
207
- if self.current_context and self.agent._should_update_context(self.current_context, "before"):
212
+ if self.current_context and self.agent._should_update_context(
213
+ self.current_context, "before"
214
+ ):
208
215
  self.current_context = self.agent._perform_context_update(
209
216
  context=self.current_context,
210
217
  model=self.model,
@@ -240,7 +247,9 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
240
247
  self.current_step += 1
241
248
 
242
249
  # Update context before processing if configured
243
- if self.current_context and self.agent._should_update_context(self.current_context, "before"):
250
+ if self.current_context and self.agent._should_update_context(
251
+ self.current_context, "before"
252
+ ):
244
253
  self.current_context = self.agent._perform_context_update(
245
254
  context=self.current_context,
246
255
  model=self.model,
@@ -283,16 +292,16 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
283
292
  """Format context for display in string representation."""
284
293
  if context is None:
285
294
  return "None"
286
-
295
+
287
296
  try:
288
297
  # For Pydantic models, show as dict
289
- if hasattr(context, 'model_dump'):
298
+ if hasattr(context, "model_dump"):
290
299
  context_dict = context.model_dump()
291
300
  elif isinstance(context, dict):
292
301
  context_dict = context
293
302
  else:
294
303
  return str(context)
295
-
304
+
296
305
  # Format as compact JSON-like string
297
306
  items = []
298
307
  for key, value in context_dict.items():
@@ -300,7 +309,7 @@ class AgentStream(BaseGenAIModelStream[AgentResponseChunk[T]], Generic[T, AgentC
300
309
  items.append(f"{key}='{value}'")
301
310
  else:
302
311
  items.append(f"{key}={value}")
303
-
312
+
304
313
  return "{" + ", ".join(items) + "}"
305
314
  except Exception:
306
315
  return str(context)
@@ -5,7 +5,10 @@ from ...._internal import create_getattr_importer
5
5
 
6
6
 
7
7
  if TYPE_CHECKING:
8
- from .model import EmbeddingModel
8
+ from .model import (
9
+ EmbeddingModel,
10
+ create_embedding_model,
11
+ )
9
12
  from .run import (
10
13
  run_embedding_model,
11
14
  async_run_embedding_model,
@@ -19,6 +22,7 @@ if TYPE_CHECKING:
19
22
 
20
23
  __all__ = [
21
24
  "EmbeddingModel",
25
+ "create_embedding_model",
22
26
  # hammad.genai.models.embeddings.run
23
27
  "run_embedding_model",
24
28
  "async_run_embedding_model",
@@ -1,7 +1,7 @@
1
1
  """hammad.genai.embedding_models.embedding_model"""
2
2
 
3
3
  import asyncio
4
- from dataclasses import dataclass
4
+ from dataclasses import dataclass, field
5
5
  from typing import Any, List, Optional
6
6
  import sys
7
7
 
@@ -26,6 +26,7 @@ from ....formatting.text import convert_to_text
26
26
  __all__ = (
27
27
  "EmbeddingModel",
28
28
  "EmbeddingModelError",
29
+ "create_embedding_model",
29
30
  )
30
31
 
31
32
 
@@ -87,7 +88,7 @@ class EmbeddingModel:
87
88
  api_version: Optional[str] = None
88
89
  """Optional API version for a custom embedding provider."""
89
90
 
90
- settings: EmbeddingModelSettings = EmbeddingModelSettings()
91
+ settings: EmbeddingModelSettings = field(default_factory=EmbeddingModelSettings)
91
92
  """Optional settings for the embedding model."""
92
93
 
93
94
  async def async_run(
@@ -195,3 +196,31 @@ class EmbeddingModel:
195
196
  format=format,
196
197
  )
197
198
  )
199
+
200
+
201
+ def create_embedding_model(
202
+ model: str | EmbeddingModelName = "openai/text-embedding-3-small",
203
+ base_url: Optional[str] = None,
204
+ api_key: Optional[str] = None,
205
+ api_version: Optional[str] = None,
206
+ api_type: Optional[str] = None,
207
+ settings: Optional[EmbeddingModelSettings] = None,
208
+ ) -> EmbeddingModel:
209
+ """Create an embedding model instance.
210
+
211
+ Args:
212
+ model (str | EmbeddingModelName) : The model to use for the embedding.
213
+ base_url (Optional[str]) : The base URL for the API.
214
+ api_key (Optional[str]) : The API key to use for the request.
215
+ api_version (Optional[str]) : The version of the API.
216
+ api_type (Optional[str]) : The API type to use for the request.
217
+ settings (Optional[EmbeddingModelSettings]) : The settings for the embedding model.
218
+ """
219
+ return EmbeddingModel(
220
+ model=model,
221
+ base_url=base_url,
222
+ api_key=api_key,
223
+ api_version=api_version,
224
+ api_type=api_type,
225
+ settings=settings or EmbeddingModelSettings(),
226
+ )