amrita_core 0.10.0.dev2__tar.gz → 0.10.2__tar.gz
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.
- {amrita_core-0.10.0.dev2/src/amrita_core.egg-info → amrita_core-0.10.2}/PKG-INFO +1 -1
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/pyproject.toml +1 -1
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/__init__.py +2 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/base/backend.py +3 -3
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/agent.py +227 -119
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/tools.py +2 -1
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/chat_object.py +53 -12
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2/src/amrita_core.egg-info}/PKG-INFO +1 -1
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/LICENSE +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/README.md +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/setup.cfg +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/_env.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/adapters/anthropic.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/adapters/openai.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/agent/context.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/agent/functions.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/agent/strategy.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/base/adapter.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/base/tokenizer.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/__init__.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/backends.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/consts.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/hooks.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/builtins/types.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/__init__.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/chat_libs.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/chat_obj_meta.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/enums.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/memory_limiter.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/config.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/consts.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/contents.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/contexts.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/dirty.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/hook/event.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/hook/exception.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/hook/on.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/libchat.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/preset.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tokenizer.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tokenizers/jieba_based.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tokenizers/simple.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tools/manager.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tools/mcp.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/tools/models.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/__init__.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/base.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/content.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/embedding.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/memory.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/message.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/preset.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/response.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/types/tool.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/utils.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core.egg-info/SOURCES.txt +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core.egg-info/dependency_links.txt +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core.egg-info/requires.txt +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core.egg-info/top_level.txt +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_adapter.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_agent.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_chatmanager.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_chatobject.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_complete_content_validation.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_content_deserialization.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_functions.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_hooks.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_integration.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_libchat.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_manager.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_mcp.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_message_deserialization.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_message_edge_cases.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_preset.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_protocol.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_register_content.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_serialization_integrity.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_simple_tool_extended.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_tools.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_types.py +0 -0
- {amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amrita_core
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: High performance, flexible, lightweight agent framework.
|
|
5
5
|
Project-URL: Homepage, https://github.com/AmritaBot/AmritaCore
|
|
6
6
|
Project-URL: Source, https://github.com/AmritaBot/AmritaCore
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from abc import abstractmethod
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
|
|
12
12
|
from amrita_core.types import MemoryModel
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class AbilityBackend:
|
|
15
|
+
class AbilityBackend(ABC):
|
|
16
16
|
@abstractmethod
|
|
17
17
|
async def load_ability_all(self, session_id: str) -> AbilityContext:
|
|
18
18
|
"""Load ability"""
|
|
@@ -28,7 +28,7 @@ class AbilityBackend:
|
|
|
28
28
|
async def load_presets(self, session_id: str) -> MultiPresetManager: ...
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class MemoryBackend:
|
|
31
|
+
class MemoryBackend(ABC):
|
|
32
32
|
@abstractmethod
|
|
33
33
|
async def load_memory(self, session_id: str) -> MemoryModel:
|
|
34
34
|
"""Load memory"""
|
|
@@ -21,6 +21,7 @@ from amrita_core.libchat import (
|
|
|
21
21
|
)
|
|
22
22
|
from amrita_core.types import (
|
|
23
23
|
CONTENT_LIST_TYPE_ITEM,
|
|
24
|
+
Function,
|
|
24
25
|
Message,
|
|
25
26
|
SendMessageWrap,
|
|
26
27
|
TextContent,
|
|
@@ -578,158 +579,230 @@ class BaseReActAgentStrategy(AgentStrategy, ABC):
|
|
|
578
579
|
"""
|
|
579
580
|
pass
|
|
580
581
|
|
|
582
|
+
async def _run_tool_calls_concurrently(
|
|
583
|
+
self,
|
|
584
|
+
tool_calls: list[ToolCall],
|
|
585
|
+
) -> list[tuple[ToolCall, str, BaseException | None]]:
|
|
586
|
+
"""Execute multiple tool calls concurrently via asyncio.gather.
|
|
587
|
+
|
|
588
|
+
Built-in flow-control tools (REASONING, STOP) are dispatched to their
|
|
589
|
+
specialised handlers inside each coroutine. Regular tools use
|
|
590
|
+
:meth:`call_tool`. One failure does not cancel the others.
|
|
591
|
+
Results are returned in the same order as the input list.
|
|
592
|
+
|
|
593
|
+
Subclasses may override this to change the execution strategy (e.g. add
|
|
594
|
+
throttling, retries, or switch to sequential fallback).
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
tool_calls: ToolCall objects to execute concurrently. Callers should
|
|
598
|
+
order them so that built-in tools appear **after** regular tools
|
|
599
|
+
so that the side-effects of built-in handlers do not race with
|
|
600
|
+
the context modifications produced by regular-tool result
|
|
601
|
+
processing.
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
List of ``(tool_call, result_string, original_exception)`` tuples.
|
|
605
|
+
``original_exception`` is the exception instance when an error
|
|
606
|
+
occurred, ``None`` otherwise. On error ``result_string`` is
|
|
607
|
+
formatted as ``"ERR: Tool {name} execution failed\\n{error}"``.
|
|
608
|
+
"""
|
|
609
|
+
|
|
610
|
+
async def _exec_one(tc: ToolCall) -> tuple[ToolCall, str, BaseException | None]:
|
|
611
|
+
fn = tc.function.name
|
|
612
|
+
try:
|
|
613
|
+
if fn == REASONING_TOOL.function.name:
|
|
614
|
+
content: UniResponse[
|
|
615
|
+
str, None
|
|
616
|
+
] = await self._generate_reasoning_content(
|
|
617
|
+
tc, self.ctx.original_context.unwrap()
|
|
618
|
+
)
|
|
619
|
+
await self._append_reasoning(tc, content)
|
|
620
|
+
return (tc, content.content, None)
|
|
621
|
+
if fn == STOP_TOOL.function.name:
|
|
622
|
+
args: dict[str, Any] = json.loads(tc.function.arguments)
|
|
623
|
+
self.agent_last_step = "Stopped"
|
|
624
|
+
self.reasoning_pc = 0
|
|
625
|
+
self._suggested_stop = True
|
|
626
|
+
logger.info("Agent work has been terminated.")
|
|
627
|
+
|
|
628
|
+
if self._should_enable_reflection():
|
|
629
|
+
logger.debug("Running post-reasoning reflection...")
|
|
630
|
+
reason_ctx = self.ctx.original_context.unwrap()
|
|
631
|
+
reflection_results = await self._run_reflection(reason_ctx)
|
|
632
|
+
failures = [
|
|
633
|
+
r for r in reflection_results if r["result"] == "fail"
|
|
634
|
+
]
|
|
635
|
+
if failures:
|
|
636
|
+
correction_msg = (
|
|
637
|
+
"<BEGIN_OF_REFLECTION_CORRECTION>\n"
|
|
638
|
+
+ "Your reasoning was checked and the following issues were found:\n"
|
|
639
|
+
+ "\n".join(
|
|
640
|
+
f"- [{r['check_type']}] {r['detail']}"
|
|
641
|
+
for r in failures
|
|
642
|
+
)
|
|
643
|
+
+ "\nPlease re-examine your reasoning and correct these issues "
|
|
644
|
+
+ "before providing the final answer.\n"
|
|
645
|
+
+ "<END_OF_REFLECTION_CORRECTION>"
|
|
646
|
+
)
|
|
647
|
+
self.ctx.message.append(
|
|
648
|
+
Message(role="user", content=correction_msg)
|
|
649
|
+
)
|
|
650
|
+
# Empty result → caller skips stop response.
|
|
651
|
+
return (tc, "", None)
|
|
652
|
+
|
|
653
|
+
result = self._build_stop_response(args)
|
|
654
|
+
return (tc, result, None)
|
|
655
|
+
|
|
656
|
+
# Regular tool
|
|
657
|
+
result = await self.call_tool(tc)
|
|
658
|
+
return (tc, result, None)
|
|
659
|
+
except Exception as e:
|
|
660
|
+
error_content = await self._handle_tool_error_common(fn, e, tc.id)
|
|
661
|
+
return (tc, error_content, e)
|
|
662
|
+
|
|
663
|
+
return await asyncio.gather(*[_exec_one(tc) for tc in tool_calls])
|
|
664
|
+
|
|
581
665
|
async def _execute_tool_loop(
|
|
582
666
|
self,
|
|
583
667
|
response_msg: UniResponse[None, list[ToolCall] | None],
|
|
584
668
|
) -> bool:
|
|
585
|
-
"""Execute the main tool calling loop
|
|
669
|
+
"""Execute the main tool calling loop.
|
|
586
670
|
|
|
587
|
-
|
|
588
|
-
|
|
671
|
+
All tool calls are executed concurrently via
|
|
672
|
+
:meth:`_run_tool_calls_concurrently`. Built-in tools are ordered last
|
|
673
|
+
so that their message-list side-effects (reasoning append, stop
|
|
674
|
+
response) are applied after regular-tool results.
|
|
589
675
|
|
|
590
676
|
Args:
|
|
591
|
-
response_msg: The response from tools_caller containing tool calls
|
|
677
|
+
response_msg: The response from tools_caller containing tool calls.
|
|
592
678
|
|
|
593
679
|
Returns:
|
|
594
|
-
True if execution should continue, False if it should stop
|
|
680
|
+
True if execution should continue, False if it should stop.
|
|
595
681
|
"""
|
|
596
682
|
if not (tool_calls := response_msg.tool_calls):
|
|
597
683
|
return False
|
|
598
684
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
685
|
+
# Built-in tools last so their side-effects don't race with regular tools.
|
|
686
|
+
tool_calls.sort(
|
|
687
|
+
key=lambda tc: (
|
|
688
|
+
tc.function.name
|
|
689
|
+
in (REASONING_TOOL.function.name, STOP_TOOL.function.name)
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# Notify "calling" for every tool
|
|
694
|
+
for tc in tool_calls:
|
|
606
695
|
await self.chat_object.io_stream.yield_response(
|
|
607
696
|
MessageWithMetadata(
|
|
608
|
-
content=f"Calling function {
|
|
697
|
+
content=f"Calling function {tc.function.name}\n",
|
|
609
698
|
metadata=AgentToolCallMetadata(
|
|
610
699
|
type="function_call",
|
|
611
700
|
extra_type=None,
|
|
612
|
-
function_name=
|
|
701
|
+
function_name=tc.function.name,
|
|
613
702
|
is_done=False,
|
|
614
|
-
tool_id=
|
|
703
|
+
tool_id=tc.id,
|
|
615
704
|
err=None,
|
|
616
705
|
),
|
|
617
706
|
)
|
|
618
707
|
)
|
|
619
708
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
correction_msg = (
|
|
648
|
-
"<BEGIN_OF_REFLECTION_CORRECTION>\n"
|
|
649
|
-
+ "Your reasoning was checked and the following issues were found:\n"
|
|
650
|
-
+ "\n".join(
|
|
651
|
-
f"- [{r['check_type']}] {r['detail']}"
|
|
652
|
-
for r in failures
|
|
653
|
-
)
|
|
654
|
-
+ "\nPlease re-examine your reasoning and correct these issues "
|
|
655
|
-
+ "before providing the final answer.\n"
|
|
656
|
-
+ "<END_OF_REFLECTION_CORRECTION>"
|
|
657
|
-
)
|
|
658
|
-
self.ctx.message.append(
|
|
659
|
-
Message(
|
|
660
|
-
role="user",
|
|
661
|
-
content=correction_msg,
|
|
662
|
-
)
|
|
663
|
-
)
|
|
664
|
-
# Don't build stop response yet — let the agent re-reason
|
|
665
|
-
continue
|
|
666
|
-
|
|
667
|
-
func_response = self._build_stop_response(function_args)
|
|
668
|
-
await self._build_stop_response_and_append(
|
|
669
|
-
function_args,
|
|
670
|
-
response_msg,
|
|
671
|
-
function_name,
|
|
672
|
-
tool_call.id,
|
|
673
|
-
func_response,
|
|
674
|
-
)
|
|
675
|
-
case _:
|
|
676
|
-
self.reasoning_pc = 0
|
|
677
|
-
func_response = await self.call_tool(tool_call)
|
|
678
|
-
await self._append_tool_result_to_context(
|
|
679
|
-
tool_call, func_response, response_msg
|
|
680
|
-
)
|
|
681
|
-
except Exception as err:
|
|
682
|
-
error_content = await self._handle_tool_error_common(
|
|
683
|
-
function_name, err, tool_call.id
|
|
709
|
+
concurrent_results: list[
|
|
710
|
+
tuple[ToolCall, str, BaseException | None]
|
|
711
|
+
] = await self._run_tool_calls_concurrently(tool_calls)
|
|
712
|
+
|
|
713
|
+
# Append results sequentially
|
|
714
|
+
result_msg_list: list[ToolResult] = []
|
|
715
|
+
should_continue = True # default: keep looping (old `ret` semantics)
|
|
716
|
+
for tc, func_response, exc in concurrent_results:
|
|
717
|
+
function_name = tc.function.name
|
|
718
|
+
is_reasoning = function_name == REASONING_TOOL.function.name
|
|
719
|
+
is_stop = function_name == STOP_TOOL.function.name
|
|
720
|
+
|
|
721
|
+
if is_reasoning:
|
|
722
|
+
# _run_tool_calls_concurrently already called _append_reasoning.
|
|
723
|
+
should_continue = True
|
|
724
|
+
continue
|
|
725
|
+
|
|
726
|
+
if is_stop:
|
|
727
|
+
if not func_response:
|
|
728
|
+
# Reflection failed — correction already injected, continue.
|
|
729
|
+
continue
|
|
730
|
+
await self._build_stop_response_and_append(
|
|
731
|
+
json.loads(tc.function.arguments),
|
|
732
|
+
response_msg,
|
|
733
|
+
function_name,
|
|
734
|
+
tc.id,
|
|
735
|
+
func_response,
|
|
684
736
|
)
|
|
685
|
-
|
|
737
|
+
elif func_response.startswith("ERR:"):
|
|
738
|
+
self.reasoning_pc = 0
|
|
686
739
|
await self._handle_error_append(
|
|
687
740
|
function_name,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
original_exception=
|
|
741
|
+
func_response,
|
|
742
|
+
tc.id,
|
|
743
|
+
original_exception=exc,
|
|
691
744
|
)
|
|
692
745
|
else:
|
|
693
|
-
|
|
694
|
-
|
|
746
|
+
self.reasoning_pc = 0
|
|
747
|
+
await self._append_tool_result_to_context(
|
|
748
|
+
tc, func_response, response_msg
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
logger.debug(f"Function {function_name} returned: {func_response}")
|
|
752
|
+
result_msg_list.append(
|
|
753
|
+
ToolResult(
|
|
695
754
|
role="tool",
|
|
696
755
|
content=func_response,
|
|
697
756
|
name=function_name,
|
|
698
|
-
tool_call_id=
|
|
757
|
+
tool_call_id=tc.id,
|
|
699
758
|
)
|
|
700
|
-
|
|
759
|
+
)
|
|
760
|
+
self.call_count += 1
|
|
701
761
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
error=prompt,
|
|
715
|
-
),
|
|
716
|
-
)
|
|
762
|
+
prompt = self._check_and_handle_loop_reasoning()
|
|
763
|
+
if prompt is not None:
|
|
764
|
+
await self._handle_loop_reasoning_cleanup(prompt)
|
|
765
|
+
await self.chat_object.io_stream.yield_response(
|
|
766
|
+
MessageWithMetadata(
|
|
767
|
+
content=prompt,
|
|
768
|
+
metadata=AgentLoopErrorMetadata(
|
|
769
|
+
type="error",
|
|
770
|
+
extra_type="loop_reasoning",
|
|
771
|
+
chat_object_id=self.chat_object.stream_id,
|
|
772
|
+
error=prompt,
|
|
773
|
+
),
|
|
717
774
|
)
|
|
718
|
-
|
|
775
|
+
)
|
|
776
|
+
# Stop early — don't process remaining results.
|
|
777
|
+
should_continue = False
|
|
778
|
+
break
|
|
719
779
|
|
|
720
|
-
|
|
721
|
-
|
|
780
|
+
# Notify once with the complete result list.
|
|
781
|
+
if result_msg_list:
|
|
782
|
+
await self._notify_tool_calls(
|
|
783
|
+
result_msg_list,
|
|
784
|
+
result_msg_list[-1].name,
|
|
785
|
+
result_msg_list[-1].tool_call_id,
|
|
786
|
+
)
|
|
722
787
|
|
|
723
|
-
return
|
|
788
|
+
return should_continue
|
|
724
789
|
|
|
725
790
|
async def _handle_error_append(
|
|
726
791
|
self,
|
|
727
792
|
function_name: str,
|
|
728
793
|
error_content: str,
|
|
729
794
|
tool_call_id: str,
|
|
730
|
-
original_exception: BaseException,
|
|
795
|
+
original_exception: BaseException | None = None,
|
|
731
796
|
):
|
|
732
|
-
"""Handle appending error messages to context (strategy-specific).
|
|
797
|
+
"""Handle appending error messages to context (strategy-specific).
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
function_name: Name of the failed function.
|
|
801
|
+
error_content: Formatted error message to append.
|
|
802
|
+
tool_call_id: ID of the failed tool call.
|
|
803
|
+
original_exception: The original exception, or ``None`` when the error
|
|
804
|
+
was captured as a string during concurrent execution.
|
|
805
|
+
"""
|
|
733
806
|
...
|
|
734
807
|
|
|
735
808
|
@abstractmethod
|
|
@@ -1127,9 +1200,25 @@ class ReActAgentStrategy(BaseReActAgentStrategy):
|
|
|
1127
1200
|
function_call_id: str,
|
|
1128
1201
|
function_response: str,
|
|
1129
1202
|
):
|
|
1130
|
-
"""ReAct strategy: append assistant message before
|
|
1203
|
+
"""ReAct strategy: append assistant message with only this STOP tool_call before its ToolResult.
|
|
1204
|
+
|
|
1205
|
+
Only a single ToolCall is included in the assistant message to avoid the
|
|
1206
|
+
"insufficient tool messages following tool_calls message" API error.
|
|
1207
|
+
"""
|
|
1131
1208
|
self.ctx.message.append(
|
|
1132
|
-
Message
|
|
1209
|
+
Message(
|
|
1210
|
+
role="assistant",
|
|
1211
|
+
content=None,
|
|
1212
|
+
tool_calls=[
|
|
1213
|
+
ToolCall(
|
|
1214
|
+
id=function_call_id,
|
|
1215
|
+
function=Function(
|
|
1216
|
+
name=function_name,
|
|
1217
|
+
arguments=json.dumps(function_args),
|
|
1218
|
+
),
|
|
1219
|
+
)
|
|
1220
|
+
],
|
|
1221
|
+
)
|
|
1133
1222
|
)
|
|
1134
1223
|
self.ctx.message.append(
|
|
1135
1224
|
ToolResult(
|
|
@@ -1147,14 +1236,16 @@ class ReActAgentStrategy(BaseReActAgentStrategy):
|
|
|
1147
1236
|
func_response: str,
|
|
1148
1237
|
response_msg: UniResponse[None, list[ToolCall] | None],
|
|
1149
1238
|
):
|
|
1150
|
-
"""ReAct strategy: append
|
|
1239
|
+
"""ReAct strategy: append assistant message with only this tool_call paired with its ToolResult.
|
|
1151
1240
|
|
|
1152
1241
|
This follows OpenAI's ToolCall-ToolResult pairing requirement where every
|
|
1153
1242
|
assistant message with tool_calls must be followed by corresponding tool messages.
|
|
1243
|
+
Only a single ToolCall is included per assistant message to prevent the
|
|
1244
|
+
"insufficient tool messages following tool_calls message" API error when the
|
|
1245
|
+
model returns multiple tool_calls in one response.
|
|
1154
1246
|
"""
|
|
1155
|
-
# First, append the assistant message containing the tool_calls
|
|
1156
1247
|
msg_list = self.ctx.message
|
|
1157
|
-
msg_list.append(Message
|
|
1248
|
+
msg_list.append(Message(role="assistant", content=None, tool_calls=[tool_call]))
|
|
1158
1249
|
msg_list.append(
|
|
1159
1250
|
ToolResult(
|
|
1160
1251
|
role="tool",
|
|
@@ -1170,20 +1261,36 @@ class ReActAgentStrategy(BaseReActAgentStrategy):
|
|
|
1170
1261
|
function_name: str,
|
|
1171
1262
|
error_content: str,
|
|
1172
1263
|
tool_call_id: str,
|
|
1173
|
-
original_exception: BaseException,
|
|
1264
|
+
original_exception: BaseException | None = None,
|
|
1174
1265
|
):
|
|
1175
|
-
"""ReAct strategy: append error as
|
|
1266
|
+
"""ReAct strategy: append error as an assistant+tool message pair.
|
|
1176
1267
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1268
|
+
An assistant message with a single ToolCall is prepended to satisfy the
|
|
1269
|
+
OpenAI API requirement that every ToolResult must follow an assistant
|
|
1270
|
+
message containing the corresponding tool_call.
|
|
1180
1271
|
|
|
1181
1272
|
Args:
|
|
1182
1273
|
function_name: Name of the failed function
|
|
1183
1274
|
error_content: Formatted error message to append
|
|
1184
1275
|
tool_call_id: ID of the tool call
|
|
1185
|
-
original_exception: The original exception
|
|
1276
|
+
original_exception: The original exception, or ``None`` when the
|
|
1277
|
+
error was captured as a string during concurrent execution.
|
|
1186
1278
|
"""
|
|
1279
|
+
self.ctx.message.append(
|
|
1280
|
+
Message(
|
|
1281
|
+
role="assistant",
|
|
1282
|
+
content=None,
|
|
1283
|
+
tool_calls=[
|
|
1284
|
+
ToolCall(
|
|
1285
|
+
id=tool_call_id,
|
|
1286
|
+
function=Function(
|
|
1287
|
+
name=function_name,
|
|
1288
|
+
arguments="{}",
|
|
1289
|
+
),
|
|
1290
|
+
)
|
|
1291
|
+
],
|
|
1292
|
+
)
|
|
1293
|
+
)
|
|
1187
1294
|
self.ctx.message.append(
|
|
1188
1295
|
ToolResult(
|
|
1189
1296
|
role="tool",
|
|
@@ -1193,6 +1300,7 @@ class ReActAgentStrategy(BaseReActAgentStrategy):
|
|
|
1193
1300
|
)
|
|
1194
1301
|
)
|
|
1195
1302
|
|
|
1303
|
+
@override
|
|
1196
1304
|
async def single_execute(
|
|
1197
1305
|
self,
|
|
1198
1306
|
) -> bool:
|
|
@@ -15,7 +15,8 @@ from .types import AgentMiddleMessageMetadata
|
|
|
15
15
|
|
|
16
16
|
PROCESS_MESSAGE_TOOL = FunctionDefinitionSchema(
|
|
17
17
|
name="processing_message",
|
|
18
|
-
description="Describe what the agent is currently doing and express the agent's internal
|
|
18
|
+
description="Describe what the agent is currently doing and express the agent's **internal ideas** to the user."
|
|
19
|
+
+ " Use this when you need to communicate your current actions or internal ideas to the user, **not** for general completion.",
|
|
19
20
|
parameters=FunctionParametersSchema(
|
|
20
21
|
type="object",
|
|
21
22
|
properties={
|
|
@@ -115,9 +115,9 @@ class ChatObject:
|
|
|
115
115
|
# Timing
|
|
116
116
|
timestamp: str # Timestamp (for LLM)
|
|
117
117
|
time: datetime # Time
|
|
118
|
-
end_at: datetime | None
|
|
118
|
+
end_at: datetime | None
|
|
119
119
|
last_call: datetime # Last internal function call time
|
|
120
|
-
now_calling: str | None
|
|
120
|
+
now_calling: str | None # currently calling function name
|
|
121
121
|
|
|
122
122
|
# Config & Preset
|
|
123
123
|
config: AmritaConfig # config used in this call
|
|
@@ -143,10 +143,10 @@ class ChatObject:
|
|
|
143
143
|
extra_usage: UniResponseUsage[int]
|
|
144
144
|
|
|
145
145
|
# Runtime State
|
|
146
|
-
_is_running: bool
|
|
147
|
-
_is_done: bool
|
|
146
|
+
_is_running: bool # Whether it is running
|
|
147
|
+
_is_done: bool # Whether it has completed
|
|
148
148
|
_task: Task[None] # (lateinit) set on runtime
|
|
149
|
-
_err: BaseException | None
|
|
149
|
+
_err: BaseException | None # Exception in runtime
|
|
150
150
|
|
|
151
151
|
# Options
|
|
152
152
|
_bke_opt: DatabackendOptions
|
|
@@ -156,21 +156,53 @@ class ChatObject:
|
|
|
156
156
|
_raised_exc: tuple[type[BaseException], ...]
|
|
157
157
|
|
|
158
158
|
# Workflow / Interpreter
|
|
159
|
-
_workflow:
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
_interpreter: (
|
|
163
|
-
WorkflowInterpreter # (lateinit) When _entry is called, this will be set.
|
|
164
|
-
)
|
|
159
|
+
_workflow: NodeComposeRendered
|
|
160
|
+
_interpreter: WorkflowInterpreter
|
|
165
161
|
_middleware: (
|
|
166
162
|
Callable[[Self], Awaitable[Any]] | None
|
|
167
163
|
) # Middleware for the whole workflow, will be set in __init__.
|
|
168
164
|
|
|
169
165
|
# Agent loop state
|
|
170
|
-
_agent_loop: AgentLoopState | None
|
|
166
|
+
_agent_loop: AgentLoopState | None
|
|
171
167
|
|
|
172
168
|
# ChatObject temp storage
|
|
173
169
|
_chatman: ChatManager
|
|
170
|
+
__slots__ = (
|
|
171
|
+
"_agent_loop",
|
|
172
|
+
"_bke_opt",
|
|
173
|
+
"_chatman",
|
|
174
|
+
"_err",
|
|
175
|
+
"_hook_args",
|
|
176
|
+
"_hook_kwargs",
|
|
177
|
+
"_interpreter",
|
|
178
|
+
"_is_done",
|
|
179
|
+
"_is_running",
|
|
180
|
+
"_middleware",
|
|
181
|
+
"_raised_exc",
|
|
182
|
+
"_s_id",
|
|
183
|
+
"_task",
|
|
184
|
+
"_workflow",
|
|
185
|
+
"config",
|
|
186
|
+
"context_wrap",
|
|
187
|
+
"end_at",
|
|
188
|
+
"extra_usage",
|
|
189
|
+
"io_stream",
|
|
190
|
+
"jinja2_vars",
|
|
191
|
+
"last_call",
|
|
192
|
+
"now_calling",
|
|
193
|
+
"preset",
|
|
194
|
+
"response",
|
|
195
|
+
"slot",
|
|
196
|
+
"state",
|
|
197
|
+
"strategy",
|
|
198
|
+
"stream_id",
|
|
199
|
+
"template",
|
|
200
|
+
"time",
|
|
201
|
+
"timestamp",
|
|
202
|
+
"train",
|
|
203
|
+
"user_input",
|
|
204
|
+
"user_message",
|
|
205
|
+
)
|
|
174
206
|
|
|
175
207
|
def __init__(
|
|
176
208
|
self,
|
|
@@ -226,6 +258,13 @@ class ChatObject:
|
|
|
226
258
|
backend_options: Fine-grained control over which backend
|
|
227
259
|
fetch/commit operations are performed.
|
|
228
260
|
"""
|
|
261
|
+
# Init as None in slots
|
|
262
|
+
self._agent_loop = None
|
|
263
|
+
self._err = None
|
|
264
|
+
self._is_done = False
|
|
265
|
+
self._is_running = False
|
|
266
|
+
self.now_calling = None
|
|
267
|
+
self.end_at = None
|
|
229
268
|
# Special flags
|
|
230
269
|
self._raised_exc = (
|
|
231
270
|
exception_ignored if not __flags__.DISABLE_EXC_IGNORED else ()
|
|
@@ -530,6 +569,8 @@ async def _load_state(chat_obj: ChatObject):
|
|
|
530
569
|
)
|
|
531
570
|
if not (opt.skip_memory_fetch):
|
|
532
571
|
chat_obj.state.memory = await slot.memory.load_memory(chat_obj.state.session_id)
|
|
572
|
+
if not hasattr(chat_obj, "preset"):
|
|
573
|
+
chat_obj.preset = chat_obj.state.ability.presets.get_default_preset()
|
|
533
574
|
|
|
534
575
|
|
|
535
576
|
@Node(SuspendEnum.TRAIN_RENDER)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amrita_core
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: High performance, flexible, lightweight agent framework.
|
|
5
5
|
Project-URL: Homepage, https://github.com/AmritaBot/AmritaCore
|
|
6
6
|
Project-URL: Source, https://github.com/AmritaBot/AmritaCore
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core/chatmanager/memory_limiter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amrita_core-0.10.0.dev2 → amrita_core-0.10.2}/src/amrita_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|