agnt5 0.2.4__cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 0.2.5__cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.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.

Potentially problematic release.


This version of agnt5 might be problematic. Click here for more details.

agnt5/__init__.py CHANGED
@@ -32,7 +32,7 @@ from .tool import Tool, ToolRegistry, tool
32
32
  from .types import BackoffPolicy, BackoffType, FunctionConfig, RetryPolicy, WorkflowConfig
33
33
  from .version import _get_version
34
34
  from .worker import Worker
35
- from .workflow import WorkflowRegistry, workflow
35
+ from .workflow import WorkflowRegistry, chatflow, workflow
36
36
 
37
37
  # Expose simplified language model API (recommended)
38
38
  from . import lm
@@ -57,6 +57,7 @@ __all__ = [
57
57
  "with_entity_context",
58
58
  "create_entity_context",
59
59
  "workflow",
60
+ "chatflow",
60
61
  "WorkflowRegistry",
61
62
  "tool",
62
63
  "Tool",
agnt5/_core.abi3.so CHANGED
Binary file
agnt5/agent.py CHANGED
@@ -377,13 +377,16 @@ class Agent:
377
377
  if handoff_config.pass_full_history:
378
378
  # Get current conversation from the agent's run loop
379
379
  # (This will be set when we detect the handoff in run())
380
- conversation_history = ctx.get("_current_conversation", [])
380
+ conversation_history = getattr(ctx, '_agent_data', {}).get("_current_conversation", [])
381
+
381
382
  if conversation_history:
382
383
  ctx.logger.info(
383
384
  f"Passing {len(conversation_history)} messages to target agent"
384
385
  )
385
386
  # Store in context for target agent to optionally use
386
- ctx.set("_handoff_conversation_history", conversation_history)
387
+ if not hasattr(ctx, '_agent_data'):
388
+ ctx._agent_data = {}
389
+ ctx._agent_data["_handoff_conversation_history"] = conversation_history
387
390
 
388
391
  # Execute target agent with the message and shared context
389
392
  result = await target_agent.run(message, context=ctx)
@@ -398,7 +401,9 @@ class Agent:
398
401
  "tool_calls": result.tool_calls,
399
402
  }
400
403
 
401
- ctx.set("_handoff_result", handoff_data)
404
+ if not hasattr(ctx, '_agent_data'):
405
+ ctx._agent_data = {}
406
+ ctx._agent_data["_handoff_result"] = handoff_data
402
407
 
403
408
  # Return the handoff data (will be detected in run() loop)
404
409
  return handoff_data
@@ -449,7 +454,6 @@ class Agent:
449
454
 
450
455
  context = Context(
451
456
  run_id=f"agent-{self.name}-{uuid.uuid4().hex[:8]}",
452
- component_type="agent",
453
457
  )
454
458
 
455
459
  # Create span for agent execution with trace linking
@@ -471,8 +475,6 @@ class Agent:
471
475
 
472
476
  # Reasoning loop
473
477
  for iteration in range(self.max_iterations):
474
- self.logger.info(f"Agent iteration {iteration + 1}/{self.max_iterations}")
475
-
476
478
  # Build tool definitions for LLM
477
479
  tool_defs = [
478
480
  ToolDefinition(
@@ -517,10 +519,13 @@ class Agent:
517
519
 
518
520
  # Check if LLM wants to use tools
519
521
  if response.tool_calls:
520
- self.logger.info(f"Agent calling {len(response.tool_calls)} tool(s)")
522
+ self.logger.debug(f"Agent calling {len(response.tool_calls)} tool(s)")
521
523
 
522
524
  # Store current conversation in context for potential handoffs
523
- context.set("_current_conversation", messages)
525
+ # Use a simple dict attribute since we don't need full state persistence for this
526
+ if not hasattr(context, '_agent_data'):
527
+ context._agent_data = {}
528
+ context._agent_data["_current_conversation"] = messages
524
529
 
525
530
  # Execute tool calls
526
531
  tool_results = []
@@ -586,13 +591,13 @@ class Agent:
586
591
  for tr in tool_results
587
592
  ]
588
593
  )
589
- messages.append(Message.user(f"Tool results:\n{results_text}"))
594
+ messages.append(Message.user(f"Tool results:\n{results_text}\n\nPlease provide your final answer based on these results."))
590
595
 
591
596
  # Continue loop for agent to process results
592
597
 
593
598
  else:
594
599
  # No tool calls - agent is done
595
- self.logger.info(f"Agent completed after {iteration + 1} iterations")
600
+ self.logger.debug(f"Agent completed after {iteration + 1} iterations")
596
601
  return AgentResult(
597
602
  output=response.text,
598
603
  tool_calls=all_tool_calls,
@@ -636,7 +641,6 @@ class Agent:
636
641
 
637
642
  context = Context(
638
643
  run_id=f"agent-chat-{self.name}-{uuid.uuid4().hex[:8]}",
639
- component_type="agent",
640
644
  )
641
645
 
642
646
  # Add user message
agnt5/lm.py CHANGED
@@ -312,11 +312,23 @@ class _LanguageModel:
312
312
  if request.response_schema is not None:
313
313
  kwargs["response_schema_kw"] = request.response_schema
314
314
 
315
- # TODO: Add tools and tool_choice support when needed
316
- # if request.tools:
317
- # kwargs["tools"] = self._serialize_tools(request.tools)
318
- # if request.tool_choice:
319
- # kwargs["tool_choice"] = request.tool_choice.value
315
+ # Pass tools and tool_choice to Rust
316
+ if request.tools:
317
+ # Serialize tools to JSON for Rust
318
+ tools_list = [
319
+ {
320
+ "name": tool.name,
321
+ "description": tool.description,
322
+ "parameters": tool.parameters,
323
+ }
324
+ for tool in request.tools
325
+ ]
326
+ tools_json = json.dumps(tools_list)
327
+ kwargs["tools"] = tools_json
328
+
329
+ if request.tool_choice:
330
+ # Serialize tool_choice to JSON for Rust
331
+ kwargs["tool_choice"] = json.dumps(request.tool_choice.value)
320
332
 
321
333
  # Call Rust implementation - it returns a proper Python coroutine now
322
334
  # Using pyo3-async-runtimes for truly async HTTP calls without blocking
@@ -362,11 +374,22 @@ class _LanguageModel:
362
374
  if request.config.top_p is not None:
363
375
  kwargs["top_p"] = request.config.top_p
364
376
 
365
- # TODO: Add tools and tool_choice support when needed
366
- # if request.tools:
367
- # kwargs["tools"] = self._serialize_tools(request.tools)
368
- # if request.tool_choice:
369
- # kwargs["tool_choice"] = request.tool_choice.value
377
+ # Pass tools and tool_choice to Rust
378
+ if request.tools:
379
+ # Serialize tools to JSON for Rust
380
+ tools_list = [
381
+ {
382
+ "name": tool.name,
383
+ "description": tool.description,
384
+ "parameters": tool.parameters,
385
+ }
386
+ for tool in request.tools
387
+ ]
388
+ kwargs["tools"] = json.dumps(tools_list)
389
+
390
+ if request.tool_choice:
391
+ # Serialize tool_choice to JSON for Rust
392
+ kwargs["tool_choice"] = json.dumps(request.tool_choice.value)
370
393
 
371
394
  # Call Rust implementation - it returns a proper Python coroutine now
372
395
  # Using pyo3-async-runtimes for truly async streaming without blocking
@@ -416,11 +439,16 @@ class _LanguageModel:
416
439
  total_tokens=rust_response.usage.total_tokens,
417
440
  )
418
441
 
442
+ # Extract tool_calls from Rust response
443
+ tool_calls = None
444
+ if hasattr(rust_response, 'tool_calls') and rust_response.tool_calls:
445
+ tool_calls = rust_response.tool_calls
446
+
419
447
  return GenerateResponse(
420
448
  text=rust_response.content,
421
449
  usage=usage,
422
450
  finish_reason=None, # TODO: Add finish_reason to Rust response
423
- tool_calls=None, # TODO: Add tool calls support
451
+ tool_calls=tool_calls,
424
452
  _rust_response=rust_response, # Store for .structured_output access
425
453
  )
426
454
 
agnt5/workflow.py CHANGED
@@ -565,3 +565,121 @@ def workflow(
565
565
  return decorator
566
566
  else:
567
567
  return decorator(_func)
568
+
569
+
570
+ def chatflow(
571
+ _func: Optional[Callable[..., Any]] = None,
572
+ *,
573
+ name: Optional[str] = None,
574
+ ) -> Callable[..., Any]:
575
+ """
576
+ Decorator to mark a function as an AGNT5 chat-enabled workflow.
577
+
578
+ Identical to @workflow but adds metadata {"chat": "true"} to indicate
579
+ this workflow is designed for multi-turn conversation scenarios.
580
+
581
+ The platform can use this metadata to:
582
+ - Enable session affinity and sticky routing
583
+ - Apply conversation-specific optimizations
584
+ - Track chat-specific metrics (turn count, conversation length)
585
+
586
+ Args:
587
+ name: Custom workflow name (default: function's __name__)
588
+
589
+ Example:
590
+ @chatflow
591
+ async def customer_support_chat(ctx: WorkflowContext, message: str) -> dict:
592
+ # Initialize conversation state
593
+ if not ctx.state.get("messages"):
594
+ ctx.state.set("messages", [])
595
+
596
+ # Add user message
597
+ messages = ctx.state.get("messages")
598
+ messages.append({"role": "user", "content": message})
599
+ ctx.state.set("messages", messages)
600
+
601
+ # Generate AI response
602
+ response = await ctx.task(generate_response, messages=messages)
603
+
604
+ # Add assistant response
605
+ messages.append({"role": "assistant", "content": response})
606
+ ctx.state.set("messages", messages)
607
+
608
+ return {"response": response, "turn_count": len(messages) // 2}
609
+ """
610
+
611
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
612
+ # Get workflow name
613
+ workflow_name = name or func.__name__
614
+
615
+ # Validate function signature
616
+ sig = inspect.signature(func)
617
+ params = list(sig.parameters.values())
618
+
619
+ if not params or params[0].name != "ctx":
620
+ raise ValueError(
621
+ f"Chatflow '{workflow_name}' must have 'ctx: WorkflowContext' as first parameter"
622
+ )
623
+
624
+ # Convert sync to async if needed
625
+ if inspect.iscoroutinefunction(func):
626
+ handler_func = cast(HandlerFunc, func)
627
+ else:
628
+ # Wrap sync function in async
629
+ @functools.wraps(func)
630
+ async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
631
+ return func(*args, **kwargs)
632
+
633
+ handler_func = cast(HandlerFunc, async_wrapper)
634
+
635
+ # Extract schemas from type hints
636
+ input_schema, output_schema = extract_function_schemas(func)
637
+
638
+ # Extract metadata (description, etc.)
639
+ metadata = extract_function_metadata(func)
640
+
641
+ # Add chat metadata - THIS IS THE KEY DIFFERENCE FROM @workflow
642
+ metadata["chat"] = "true"
643
+
644
+ # Register as workflow (chatflows are workflows with chat metadata)
645
+ config = WorkflowConfig(
646
+ name=workflow_name,
647
+ handler=handler_func,
648
+ input_schema=input_schema,
649
+ output_schema=output_schema,
650
+ metadata=metadata,
651
+ )
652
+ WorkflowRegistry.register(config)
653
+
654
+ # Create wrapper that provides context
655
+ @functools.wraps(func)
656
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
657
+ # Create WorkflowEntity and WorkflowContext if not provided
658
+ if not args or not isinstance(args[0], WorkflowContext):
659
+ # Auto-create workflow entity and context for direct chatflow calls
660
+ run_id = f"chatflow-{uuid.uuid4().hex[:8]}"
661
+
662
+ # Create WorkflowEntity to manage state
663
+ workflow_entity = WorkflowEntity(run_id=run_id)
664
+
665
+ # Create WorkflowContext that wraps the entity
666
+ ctx = WorkflowContext(
667
+ workflow_entity=workflow_entity,
668
+ run_id=run_id,
669
+ )
670
+
671
+ # Execute chatflow
672
+ return await handler_func(ctx, *args, **kwargs)
673
+ else:
674
+ # WorkflowContext provided - use it
675
+ return await handler_func(*args, **kwargs)
676
+
677
+ # Store config on wrapper for introspection
678
+ wrapper._agnt5_config = config # type: ignore
679
+ return wrapper
680
+
681
+ # Handle both @chatflow and @chatflow(...) syntax
682
+ if _func is None:
683
+ return decorator
684
+ else:
685
+ return decorator(_func)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agnt5
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,22 +1,22 @@
1
- agnt5-0.2.4.dist-info/METADATA,sha256=0WFxoXI9jA26vzUKQp2Oqj8ZCb7H50v9PR7pxpXNXgo,994
2
- agnt5-0.2.4.dist-info/WHEEL,sha256=vPzKFEO7wksbfTgw4nRZQDJV6CVFlNP0q5TJ-KXICV8,129
3
- agnt5/__init__.py,sha256=NAziyM0ZKahdzqAP2edFHTbVHYVdIjEGXm0oEHdmiuo,2011
1
+ agnt5-0.2.5.dist-info/METADATA,sha256=zHVYaWjmmQbnDeJvr59rQNan70yGDUzjKg4cyPLfGr8,994
2
+ agnt5-0.2.5.dist-info/WHEEL,sha256=vPzKFEO7wksbfTgw4nRZQDJV6CVFlNP0q5TJ-KXICV8,129
3
+ agnt5/__init__.py,sha256=XUixnW140lI-_9h1rLtN4jBWXIDL3EgnM7jmiNzGrYk,2037
4
4
  agnt5/_compat.py,sha256=BGuy3v5VDOHVa5f3Z-C22iMN19lAt0mPmXwF3qSSWxI,369
5
- agnt5/_core.abi3.so,sha256=dz8cYuaXzda-soGA_gxOaLk-ZuvFGv5Gf3kX6eTGDFg,10221184
5
+ agnt5/_core.abi3.so,sha256=GMfOB7AQBzpNzIrZsWhnI5qOMiFtmAUCEE4p3begmHQ,10246688
6
6
  agnt5/_retry_utils.py,sha256=loHsWY5BR4wZy57IzcDEjQAy88DHVwVIr25Cn1d9GPA,5801
7
7
  agnt5/_schema_utils.py,sha256=MR67RW757T4Oq2Jqf4kB61H_b51zwaf3CLWELnkngRo,9572
8
8
  agnt5/_telemetry.py,sha256=bIY9AvBRjJBTHoBPbfR6X1OgaiUf-T0vCoi0_snsWXA,5957
9
- agnt5/agent.py,sha256=VQqNSd9o3tezxEo6jpWQyk399Wue7NgCpZvuQ-LtiTI,27494
9
+ agnt5/agent.py,sha256=BAhYHKD5YuZXhNZaqeoN7EXKTtzF8OPhCJhpcWQS1YM,27837
10
10
  agnt5/client.py,sha256=kXksazgxdVXWaG9OkjJA4cWruNtcS-ENhtnkrIdw-Nk,23212
11
11
  agnt5/context.py,sha256=S2OzPkhn_jnqSWfT21mSYOux8vHaLKQxcAvggZDHQek,2378
12
12
  agnt5/entity.py,sha256=dhdxXUxED79u3OlX9yw-2TLCC9VqBcJqES2kx-fDChs,19041
13
13
  agnt5/exceptions.py,sha256=mZ0q-NK6OKhYxgwBJpIbgpgzk-CJaFIHDbp1EE-pS7I,925
14
14
  agnt5/function.py,sha256=f1vaAlJRwuo8cxCOGEd8XPido00mOhlPS8UJJx-6hJI,11041
15
- agnt5/lm.py,sha256=AQ8xz5u1mywBKIGwT3AJKtChVdxo5HOCvzdwZ4Ilbb0,21177
15
+ agnt5/lm.py,sha256=1ufT0TGj_Ra1FXCflgxKfMh1qPQ_DV9p7BLlZGWI4m4,22062
16
16
  agnt5/tool.py,sha256=uc4L-Q9QyLzQDe-MZKk2Wo3o5e-mK8tfaQwVDgQdouQ,13133
17
17
  agnt5/tracing.py,sha256=Mh2-OfnQM61lM_P8gxJstafdsUA8Gxoo1lP-Joxhub8,5980
18
18
  agnt5/types.py,sha256=Zb71ZMwvrt1p4SH18cAKunp2y5tao_W5_jGYaPDejQo,2840
19
19
  agnt5/version.py,sha256=rOq1mObLihnnKgKqBrwZA0zwOPudEKVFcW1a48ynkqc,573
20
20
  agnt5/worker.py,sha256=_BnqqqvQE16FuezFbPda9FoiKAwcezSaxxNjU0rTjhs,33576
21
- agnt5/workflow.py,sha256=sve85o4N16PrlIwtQOqJPsHHWgl388SV702IPEXO7A8,18528
22
- agnt5-0.2.4.dist-info/RECORD,,
21
+ agnt5/workflow.py,sha256=GGCyt34W1DTSL8zjcj54qI7SA6Fn3HyQZ1XcMTlCMdI,22748
22
+ agnt5-0.2.5.dist-info/RECORD,,
File without changes