Files
wehub-resource-sync 75c67150d0
build / build (3.13) (push) Waiting to run
release-please / release-please (push) Waiting to run
release-please / build wheels (macos-aarch64) (push) Blocked by required conditions
release-please / build wheels (macos-x86_64) (push) Blocked by required conditions
release-please / build wheels (windows-x86_64) (push) Blocked by required conditions
release-please / build wheels (linux-aarch64) (push) Blocked by required conditions
release-please / build wheels (linux-x86_64) (push) Blocked by required conditions
release-please / build sdist (push) Blocked by required conditions
release-please / publish release artifacts (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:36:10 +08:00

199 lines
6.1 KiB
Python

import asyncio
from claude_agent_sdk import (
AssistantMessage,
ClaudeAgentOptions,
ClaudeSDKClient,
ResultMessage,
TextBlock,
)
from memory.local.memorize import memorize
from memory.local.tools import _get_todos, memu_server
# Set your Anthropic API key here if it's not set in the environment variables
# os.environ["ANTHROPIC_API_KEY"] = ""
N_MESSAGES_MEMORIZE = 2
RUNNING_MEMORIZATION: asyncio.Task | None = None
async def trigger_memorize(messages: list[dict[str, any]]) -> bool:
"""Create a background task to memorize conversation messages.
Returns True if the task was successfully created and registered.
"""
global RUNNING_MEMORIZATION
try:
memorize_awaitable = memorize(messages)
RUNNING_MEMORIZATION = asyncio.create_task(memorize_awaitable)
except Exception as e:
print(f"\n[Memory] Memorization initialization failed: {e!r}")
return False
else:
print("\n[Memory] Memorization task submitted.")
return True
async def get_next_input(iteration: int) -> tuple[str | None, bool]:
"""
Get the next input for the conversation.
Returns:
tuple of (input_text, should_break)
- input_text: The user input or todo-based input, None if should continue
- should_break: True if the loop should break
"""
if iteration == 0:
return await get_user_input()
todos = await _get_todos()
print(f">>> Todos:\n{todos}\n")
print("-" * 40)
if todos and "[todo]" in todos.lower():
return f"Please continue with the following todos:\n{todos}", False
return await get_user_input()
async def get_user_input() -> tuple[str | None, bool]:
"""
Get input from the user.
Returns:
tuple of (input_text, should_break)
"""
try:
user_input = input("\nYou: ").strip()
except EOFError:
return None, True
if not user_input:
return None, False
if user_input.lower() in ("quit", "exit"):
return None, True
return user_input, False
async def process_response(client: ClaudeSDKClient) -> list[str]:
"""Process the assistant response and return collected text parts."""
assistant_text_parts: list[str] = []
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
assistant_text_parts.append(block.text)
elif isinstance(message, ResultMessage):
print(f"Result: {message.result}")
return assistant_text_parts
async def check_and_memorize(conversation_messages: list[dict[str, any]]) -> None:
"""Check if memorization threshold is reached and trigger if needed.
Skips triggering if a previous memorization task is still running.
"""
global RUNNING_MEMORIZATION
if len(conversation_messages) < N_MESSAGES_MEMORIZE:
return
# Check if there's a running memorization task
if RUNNING_MEMORIZATION is not None:
if not RUNNING_MEMORIZATION.done():
print("\n[Info] Have running memorization, skipping...")
return
# Previous task completed, check for exceptions
try:
RUNNING_MEMORIZATION.result()
except Exception as e:
print(f"\n[Memory] Memorization failed: {e!r}")
RUNNING_MEMORIZATION = None
print(f"\n[Info] Reached {N_MESSAGES_MEMORIZE} messages, triggering memorization...")
success = await trigger_memorize(conversation_messages.copy())
if success:
conversation_messages.clear()
async def run_conversation_loop(client: ClaudeSDKClient) -> list[dict[str, any]]:
"""Run the main conversation loop."""
conversation_messages: list[dict[str, any]] = []
iteration = 0
while True:
user_input, should_break = await get_next_input(iteration)
if should_break:
break
if user_input is None:
continue
conversation_messages.append({"role": "user", "content": user_input})
await client.query(user_input)
assistant_text_parts = await process_response(client)
if assistant_text_parts:
conversation_messages.append({
"role": "assistant",
"content": "\n".join(assistant_text_parts),
})
await check_and_memorize(conversation_messages)
iteration += 1
return conversation_messages
async def main():
options = ClaudeAgentOptions(
mcp_servers={"memu": memu_server},
allowed_tools=[
# "mcp__memu__memu_memory",
"mcp__memu__memu_todos",
],
)
print("Claude Autorun")
print("Type 'quit' or 'exit' to end the session.")
print("-" * 40)
async with ClaudeSDKClient(options=options) as client:
remaining_messages = await run_conversation_loop(client)
# Wait for any running memorization task to complete
global RUNNING_MEMORIZATION
if RUNNING_MEMORIZATION is not None and not RUNNING_MEMORIZATION.done():
print("\n[Info] Waiting for running memorization task to complete...")
try:
await RUNNING_MEMORIZATION
print("\n[Memory] Running memorization completed successfully.")
except Exception as e:
print(f"\n[Memory] Running memorization failed: {e!r}")
RUNNING_MEMORIZATION = None
# Memorize remaining messages and wait for completion
if remaining_messages:
print("\n[Info] Session ended, memorizing remaining messages...")
success = await trigger_memorize(remaining_messages.copy())
if success and RUNNING_MEMORIZATION is not None:
print("\n[Info] Waiting for final memorization to complete...")
try:
await RUNNING_MEMORIZATION
print("\n[Memory] Final memorization completed successfully.")
except Exception as e:
print(f"\n[Memory] Final memorization failed: {e!r}")
print("\nDone")
if __name__ == "__main__":
asyncio.run(main())