fix: formatting toolkits to be more structural and come specifically from the perspective of the agent using it.
This commit is contained in:
parent
4886c30f25
commit
532675300f
|
|
@ -0,0 +1,195 @@
|
|||
"""
|
||||
Multi-Peer Honcho + Agno Example
|
||||
|
||||
A realistic multi-agent scenario using Agno's patterns:
|
||||
- A coordinator agent routes questions to specialists
|
||||
- Each specialist has its own HonchoTools (identity)
|
||||
- All share the same session for conversation continuity
|
||||
- The coordinator uses specialists as tools
|
||||
|
||||
Environment Variables:
|
||||
OPENAI_API_KEY or LLM_OPENAI_API_KEY: OpenAI API key
|
||||
HONCHO_ENVIRONMENT: 'local' or 'production' (default: production)
|
||||
HONCHO_API_KEY: Required for production environment
|
||||
"""
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
from agno.tools import tool
|
||||
|
||||
from honcho import Honcho
|
||||
from honcho_agno import HonchoTools
|
||||
|
||||
load_dotenv()
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY") and os.getenv("LLM_OPENAI_API_KEY"):
|
||||
os.environ["OPENAI_API_KEY"] = os.getenv("LLM_OPENAI_API_KEY")
|
||||
|
||||
|
||||
def create_advisor_system(honcho_env: str, session_id: str):
|
||||
"""
|
||||
Creates a multi-agent advisory system where:
|
||||
- Each specialist agent has its own identity (HonchoTools)
|
||||
- A coordinator routes to specialists and synthesizes responses
|
||||
- All agents share the same conversation session
|
||||
"""
|
||||
model_id = os.getenv("OPENAI_MODEL", "gpt-4o")
|
||||
|
||||
# Shared Honcho client and session
|
||||
honcho = Honcho(workspace_id="advisory-system", environment=honcho_env)
|
||||
session = honcho.session(session_id)
|
||||
user_peer = honcho.peer("user")
|
||||
|
||||
# === SPECIALIST AGENTS ===
|
||||
# Each has its own identity via HonchoTools
|
||||
|
||||
tech_tools = HonchoTools(
|
||||
app_id="advisory-system",
|
||||
peer_id="tech-specialist",
|
||||
session_id=session_id,
|
||||
environment=honcho_env,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
tech_agent = Agent(
|
||||
name="Tech Specialist",
|
||||
model=OpenAIChat(id=model_id),
|
||||
tools=[tech_tools],
|
||||
description="Technical advisor for architecture, implementation, and technology choices.",
|
||||
instructions=[
|
||||
"Focus on technical feasibility and implementation details.",
|
||||
"Use get_context to understand what's been discussed.",
|
||||
"Save key technical recommendations with add_message.",
|
||||
"Be concise - you're part of a team.",
|
||||
],
|
||||
)
|
||||
|
||||
business_tools = HonchoTools(
|
||||
app_id="advisory-system",
|
||||
peer_id="business-specialist",
|
||||
session_id=session_id,
|
||||
environment=honcho_env,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
business_agent = Agent(
|
||||
name="Business Specialist",
|
||||
model=OpenAIChat(id=model_id),
|
||||
tools=[business_tools],
|
||||
description="Business advisor for strategy, market fit, and ROI.",
|
||||
instructions=[
|
||||
"Focus on business viability and market considerations.",
|
||||
"Use get_context to understand what's been discussed.",
|
||||
"Save key business insights with add_message.",
|
||||
"Be concise - you're part of a team.",
|
||||
],
|
||||
)
|
||||
|
||||
# === COORDINATOR TOOLS ===
|
||||
# Wrap specialists as tools the coordinator can invoke
|
||||
|
||||
@tool
|
||||
def consult_tech_specialist(question: str) -> str:
|
||||
"""
|
||||
Consult the technical specialist for architecture, implementation,
|
||||
or technology-related questions.
|
||||
|
||||
Args:
|
||||
question: The technical question to ask.
|
||||
|
||||
Returns:
|
||||
Technical specialist's response.
|
||||
"""
|
||||
response = tech_agent.run(question)
|
||||
return response.content
|
||||
|
||||
@tool
|
||||
def consult_business_specialist(question: str) -> str:
|
||||
"""
|
||||
Consult the business specialist for strategy, market fit,
|
||||
or ROI-related questions.
|
||||
|
||||
Args:
|
||||
question: The business question to ask.
|
||||
|
||||
Returns:
|
||||
Business specialist's response.
|
||||
"""
|
||||
response = business_agent.run(question)
|
||||
return response.content
|
||||
|
||||
# Coordinator has its own identity too
|
||||
coordinator_tools = HonchoTools(
|
||||
app_id="advisory-system",
|
||||
peer_id="coordinator",
|
||||
session_id=session_id,
|
||||
environment=honcho_env,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
coordinator = Agent(
|
||||
name="Advisory Coordinator",
|
||||
model=OpenAIChat(id=model_id),
|
||||
tools=[coordinator_tools, consult_tech_specialist, consult_business_specialist],
|
||||
description="Coordinates between specialists to provide comprehensive advice.",
|
||||
instructions=[
|
||||
"Use get_context to understand the full conversation history.",
|
||||
"Route technical questions to the tech specialist.",
|
||||
"Route business questions to the business specialist.",
|
||||
"Synthesize specialist inputs into actionable recommendations.",
|
||||
"Save your final synthesis with add_message.",
|
||||
],
|
||||
)
|
||||
|
||||
return coordinator, session, user_peer
|
||||
|
||||
|
||||
def main(test_mode: bool = False):
|
||||
honcho_env = os.getenv("HONCHO_ENVIRONMENT", "production")
|
||||
session_id = f"advisory-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
print(f"Session: {session_id}")
|
||||
print("=" * 60)
|
||||
|
||||
coordinator, session, user_peer = create_advisor_system(honcho_env, session_id)
|
||||
|
||||
if test_mode:
|
||||
# Non-interactive test
|
||||
test_question = "I want to build a SaaS product for small businesses. What should I consider?"
|
||||
print(f"\n[TEST MODE] User: {test_question}\n")
|
||||
|
||||
session.add_messages([user_peer.message(test_question)])
|
||||
response = coordinator.run(test_question)
|
||||
print(f"Advisor: {response.content}\n")
|
||||
print("=" * 60)
|
||||
print("Test completed successfully!")
|
||||
return
|
||||
|
||||
# Interactive chat loop
|
||||
print("\nAdvisory System Ready")
|
||||
print("Ask questions about building a product. Type 'quit' to exit.\n")
|
||||
|
||||
while True:
|
||||
user_input = input("You: ").strip()
|
||||
if not user_input:
|
||||
continue
|
||||
if user_input.lower() in ("quit", "exit", "q"):
|
||||
break
|
||||
|
||||
# Save user message to session
|
||||
session.add_messages([user_peer.message(user_input)])
|
||||
|
||||
# Coordinator handles routing and synthesis
|
||||
response = coordinator.run(user_input)
|
||||
print(f"\nAdvisor: {response.content}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
test_mode = "--test" in sys.argv
|
||||
main(test_mode=test_mode)
|
||||
|
|
@ -2,10 +2,15 @@
|
|||
Honcho Multi-Tool Example
|
||||
|
||||
Demonstrates using all Honcho tools with an Agno agent:
|
||||
- add_message: Store conversation history
|
||||
- add_message: Store agent responses (attributed to the toolkit's peer)
|
||||
- get_context: Retrieve session context
|
||||
- search_messages: Semantic search
|
||||
- query_user: Dialectic API queries
|
||||
- query_peer: Dialectic API queries about any peer
|
||||
|
||||
Pattern: toolkit = agent identity
|
||||
- HonchoTools represents the assistant's identity
|
||||
- User messages are added via Honcho directly
|
||||
- The agent uses tools to query context and save its responses
|
||||
|
||||
Environment Variables:
|
||||
OPENAI_API_KEY or LLM_OPENAI_API_KEY: OpenAI API key
|
||||
|
|
@ -21,6 +26,7 @@ from dotenv import load_dotenv
|
|||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
|
||||
from honcho import Honcho
|
||||
from honcho_agno import HonchoTools
|
||||
|
||||
load_dotenv()
|
||||
|
|
@ -38,16 +44,25 @@ def main():
|
|||
# Get environment settings
|
||||
honcho_env = os.getenv("HONCHO_ENVIRONMENT", "production")
|
||||
|
||||
# Setup Honcho tools with a specific session
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="travel-app",
|
||||
user_id="traveler-42",
|
||||
session_id="trip-planning-session",
|
||||
# Initialize Honcho for managing the session and user peer
|
||||
honcho = Honcho(
|
||||
workspace_id="travel-app",
|
||||
environment=honcho_env,
|
||||
)
|
||||
session = honcho.session("trip-planning-session")
|
||||
user_peer = honcho.peer("traveler-42")
|
||||
|
||||
# Pre-populate with travel preferences
|
||||
print("Adding travel preferences to memory...")
|
||||
# Setup Honcho tools - this IS the assistant's identity
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="travel-app",
|
||||
peer_id="travel-assistant", # The toolkit speaks as "travel-assistant"
|
||||
session_id="trip-planning-session",
|
||||
environment=honcho_env,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
# Pre-populate with user's travel preferences (via Honcho directly)
|
||||
print("Adding user's travel preferences to memory...")
|
||||
messages = [
|
||||
"I'm planning a trip to Japan in March",
|
||||
"I love trying authentic local cuisine",
|
||||
|
|
@ -57,8 +72,8 @@ def main():
|
|||
]
|
||||
|
||||
for msg in messages:
|
||||
result = honcho_tools.add_message(msg, role="user")
|
||||
print(f" Added: {msg[:50]}...")
|
||||
session.add_messages([user_peer.message(msg)])
|
||||
print(f" [traveler-42]: {msg[:50]}...")
|
||||
|
||||
print("\n" + "-" * 70 + "\n")
|
||||
|
||||
|
|
@ -70,13 +85,14 @@ def main():
|
|||
description=(
|
||||
"A travel planning expert with access to memory tools. "
|
||||
"Use get_context for recent conversation, search_messages to find "
|
||||
"specific preferences, and query_user to understand the traveler."
|
||||
"specific preferences, and query_peer to understand the traveler."
|
||||
),
|
||||
instructions=[
|
||||
"Always retrieve relevant context before making recommendations",
|
||||
"Use search to find specific preferences mentioned",
|
||||
"Query the user's knowledge to understand their travel style",
|
||||
"Use query_peer with target_peer_id='traveler-42' to understand their travel style",
|
||||
"Be specific and actionable in your recommendations",
|
||||
"Use add_message to save your recommendations to the conversation",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -100,6 +116,12 @@ def main():
|
|||
search_result = honcho_tools.search_messages("budget money cost", limit=5)
|
||||
print(search_result)
|
||||
|
||||
# Show full conversation context
|
||||
print("\n" + "=" * 70)
|
||||
print("FULL SESSION CONTEXT")
|
||||
print("=" * 70)
|
||||
print(honcho_tools.get_context())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
"""
|
||||
Simple Honcho + Agno Example
|
||||
|
||||
A minimal example showing how to use HonchoTools with Agno agents.
|
||||
|
||||
Environment Variables:
|
||||
OPENAI_API_KEY or LLM_OPENAI_API_KEY: OpenAI API key
|
||||
HONCHO_ENVIRONMENT: 'local' or 'production' (default: production)
|
||||
|
|
@ -10,12 +8,14 @@ Environment Variables:
|
|||
"""
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
|
||||
from honcho import Honcho
|
||||
from honcho_agno import HonchoTools
|
||||
|
||||
load_dotenv()
|
||||
|
|
@ -29,18 +29,24 @@ def main():
|
|||
# Get environment settings
|
||||
honcho_env = os.getenv("HONCHO_ENVIRONMENT", "production")
|
||||
|
||||
# Initialize Honcho tools with user context
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="agno-demo",
|
||||
user_id="demo-user",
|
||||
# Create shared session
|
||||
session_id = f"simple-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
# Initialize Honcho directly for managing user messages
|
||||
honcho = Honcho(
|
||||
workspace_id="agno-demo",
|
||||
environment=honcho_env,
|
||||
)
|
||||
session = honcho.session(session_id)
|
||||
user_peer = honcho.peer("user")
|
||||
|
||||
# Add some conversation history manually
|
||||
print("Adding conversation history...")
|
||||
honcho_tools.add_message("I'm learning Python programming", role="user")
|
||||
honcho_tools.add_message(
|
||||
"I'm also interested in web development with FastAPI", role="user"
|
||||
# Initialize HonchoTools - this IS the assistant's identity
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="agno-demo",
|
||||
peer_id="assistant", # The toolkit speaks as "assistant"
|
||||
session_id=session_id, # Same session as user
|
||||
environment=honcho_env,
|
||||
honcho_client=honcho, # Reuse client
|
||||
)
|
||||
|
||||
# Create an agent with memory tools
|
||||
|
|
@ -50,16 +56,24 @@ def main():
|
|||
tools=[honcho_tools],
|
||||
description="A programming mentor that remembers user interests and progress.",
|
||||
instructions=[
|
||||
"Use the memory tools to understand the user's background",
|
||||
"Provide personalized recommendations based on their interests",
|
||||
"Use get_context to understand the conversation history",
|
||||
"Use query_peer to ask about the user's preferences",
|
||||
"Use add_message to save your responses to the conversation",
|
||||
],
|
||||
)
|
||||
|
||||
# Add user messages via Honcho directly
|
||||
print("Adding user messages to conversation...")
|
||||
session.add_messages([
|
||||
user_peer.message("I'm learning Python programming"),
|
||||
user_peer.message("I'm also interested in web development with FastAPI"),
|
||||
])
|
||||
|
||||
# The agent can now query memories and provide personalized responses
|
||||
print("\nAsking the agent for recommendations...")
|
||||
response = agent.run(
|
||||
"Based on what you know about me, what should I learn next? "
|
||||
"Use your memory tools to check my interests first."
|
||||
"Based on what you know about the user, what should they learn next? "
|
||||
"Use get_context to see the conversation history first."
|
||||
)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
|
|
@ -67,6 +81,14 @@ def main():
|
|||
print("=" * 60)
|
||||
print(response.content)
|
||||
|
||||
# Show the full context
|
||||
print("\n" + "=" * 60)
|
||||
print("SESSION CONTEXT")
|
||||
print("=" * 60)
|
||||
print(honcho_tools.get_context())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
# Run directly - test mode is default for non-interactive execution
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -50,9 +50,25 @@ Changelog = "https://github.com/plastic-labs/honcho/blob/main/CHANGELOG.md"
|
|||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"basedpyright>=1.29.4",
|
||||
"pytest>=8.2.2",
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/honcho_agno"]
|
||||
|
||||
[tool.basedpyright]
|
||||
include = ["src", "tests", "examples"]
|
||||
venvPath = "."
|
||||
venv = ".venv"
|
||||
pythonVersion = "3.11"
|
||||
reportMissingTypeStubs = false
|
||||
reportAny = false
|
||||
reportExplicitAny = false
|
||||
reportUnusedCallResult = false
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ Honcho Agno Integration
|
|||
This package provides seamless integration between Honcho and Agno,
|
||||
enabling AI agents to maintain persistent memory across conversations.
|
||||
|
||||
Each HonchoTools instance represents ONE agent identity (peer). The toolkit
|
||||
speaks as that peer when adding messages or querying the dialectic.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from agno.agent import Agent
|
||||
|
|
@ -13,7 +16,8 @@ Example:
|
|||
# Create Honcho tools
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="my-app",
|
||||
user_id="user-123",
|
||||
peer_id="assistant", # The identity for the agent using this toolkit
|
||||
session_id="session-123",
|
||||
)
|
||||
|
||||
# Create agent with memory
|
||||
|
|
@ -24,8 +28,8 @@ Example:
|
|||
description="An assistant with persistent memory powered by Honcho.",
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
response = agent.run("Remember that I prefer Python over JavaScript")
|
||||
# Run the agent - messages saved via add_message() are attributed to "assistant"
|
||||
response = agent.run("What do you know about the user?")
|
||||
```
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@ Honcho Tools for Agno
|
|||
|
||||
This module provides a Toolkit that allows Agno agents to interact with Honcho's
|
||||
memory system, including session context, semantic search, and dialectic API.
|
||||
|
||||
Each HonchoTools instance represents ONE agent identity (peer). The toolkit
|
||||
speaks as that peer when adding messages or querying the dialectic. For
|
||||
multi-peer conversations, create separate toolkit instances or use Honcho
|
||||
directly to manage other peers.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Literal, Optional
|
||||
from typing import Any, Literal
|
||||
|
||||
from agno.tools import Toolkit
|
||||
from honcho import Honcho
|
||||
|
|
@ -19,8 +24,14 @@ class HonchoTools(Toolkit):
|
|||
"""
|
||||
Honcho toolkit for Agno agents.
|
||||
|
||||
Provides memory tools for session context retrieval, semantic search,
|
||||
message storage, and querying user knowledge via the Dialectic API.
|
||||
Each toolkit instance represents ONE agent identity. The peer_id parameter
|
||||
defines who this toolkit "speaks as" - all messages added through this
|
||||
toolkit are attributed to that peer.
|
||||
|
||||
For multi-peer conversations:
|
||||
- Create one HonchoTools per agent, each with a different peer_id
|
||||
- Share the same session_id across toolkits
|
||||
- Use Honcho directly for peers not represented by an agent
|
||||
|
||||
Example:
|
||||
```python
|
||||
|
|
@ -28,9 +39,11 @@ class HonchoTools(Toolkit):
|
|||
from agno.models.openai import OpenAIChat
|
||||
from honcho_agno import HonchoTools
|
||||
|
||||
# This toolkit IS the assistant - it speaks as "assistant"
|
||||
honcho_tools = HonchoTools(
|
||||
app_id="my-app",
|
||||
user_id="user-123",
|
||||
peer_id="assistant",
|
||||
session_id="shared-session",
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
|
|
@ -43,22 +56,25 @@ class HonchoTools(Toolkit):
|
|||
def __init__(
|
||||
self,
|
||||
app_id: str = "default",
|
||||
user_id: str = "default",
|
||||
session_id: Optional[str] = None,
|
||||
api_key: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
environment: Optional[Literal["local", "production"]] = "production",
|
||||
honcho_client: Optional[Honcho] = None,
|
||||
peer_id: str = "assistant",
|
||||
session_id: str | None = None,
|
||||
api_key: str | None = None,
|
||||
base_url: str | None = None,
|
||||
environment: Literal["local", "production"] | None = "production",
|
||||
honcho_client: Honcho | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the Honcho toolkit.
|
||||
Initialize the Honcho toolkit for a specific agent identity.
|
||||
|
||||
Args:
|
||||
app_id: Application/workspace ID for scoping operations.
|
||||
Maps to Honcho's workspace_id.
|
||||
user_id: User identifier. Creates a peer with this ID.
|
||||
peer_id: The identity this toolkit represents. All messages
|
||||
added through this toolkit are attributed to this peer.
|
||||
This is who the agent "is" in the conversation.
|
||||
session_id: Optional session ID. If not provided, a new UUID
|
||||
will be generated.
|
||||
will be generated. Share this across toolkits for multi-peer
|
||||
conversations.
|
||||
api_key: Optional API key for Honcho. If not provided, will
|
||||
attempt to read from HONCHO_API_KEY environment variable.
|
||||
base_url: Optional base URL for the Honcho API.
|
||||
|
|
@ -70,10 +86,11 @@ class HonchoTools(Toolkit):
|
|||
super().__init__(name="honcho")
|
||||
|
||||
# Initialize Honcho client
|
||||
self.honcho: Honcho
|
||||
if honcho_client is not None:
|
||||
self.honcho = honcho_client
|
||||
else:
|
||||
client_kwargs: dict = {"workspace_id": app_id}
|
||||
client_kwargs: dict[str, Any] = {"workspace_id": app_id}
|
||||
if api_key is not None:
|
||||
client_kwargs["api_key"] = api_key
|
||||
if base_url is not None:
|
||||
|
|
@ -83,49 +100,47 @@ class HonchoTools(Toolkit):
|
|||
self.honcho = Honcho(**client_kwargs)
|
||||
|
||||
# Store identifiers
|
||||
self.app_id = app_id
|
||||
self.user_id = user_id
|
||||
self.session_id = session_id or str(uuid.uuid4())
|
||||
self.app_id: str = app_id
|
||||
self.peer_id: str = peer_id
|
||||
self.session_id: str = session_id or str(uuid.uuid4())
|
||||
|
||||
# Create peers for user and assistant
|
||||
self.user = self.honcho.peer(user_id)
|
||||
self.assistant = self.honcho.peer("assistant")
|
||||
# Create the peer this toolkit represents
|
||||
# This is THE identity of this toolkit - one toolkit = one voice
|
||||
self.peer: Peer = self.honcho.peer(peer_id)
|
||||
|
||||
# Create or get session
|
||||
self.session = self.honcho.session(self.session_id)
|
||||
self.session: Session = self.honcho.session(self.session_id)
|
||||
|
||||
# Register tools
|
||||
self.register(self.add_message)
|
||||
self.register(self.get_context)
|
||||
self.register(self.search_messages)
|
||||
self.register(self.query_user)
|
||||
self.register(self.query_peer)
|
||||
|
||||
def add_message(self, content: str, role: str = "user") -> str:
|
||||
def add_message(self, content: str) -> str:
|
||||
"""
|
||||
Store a message in the current session.
|
||||
Store a message in the current session as this agent.
|
||||
|
||||
Use this tool to save important information from the conversation
|
||||
that should be remembered for future interactions.
|
||||
Use this tool to save your responses or important information
|
||||
to the conversation history. The message is attributed to this
|
||||
toolkit's peer identity.
|
||||
|
||||
Args:
|
||||
content: The message content to store.
|
||||
role: The role of the message sender. Use 'user' for user messages
|
||||
or 'assistant' for AI responses.
|
||||
|
||||
Returns:
|
||||
Confirmation message indicating the memory was saved.
|
||||
"""
|
||||
try:
|
||||
peer = self.user if role == "user" else self.assistant
|
||||
self.session.add_messages([peer.message(content)])
|
||||
return f"Message saved successfully to session {self.session_id}"
|
||||
self.session.add_messages([self.peer.message(content)])
|
||||
return f"Message saved as '{self.peer_id}' to session {self.session_id}"
|
||||
except Exception as e:
|
||||
logger.exception("Error saving message")
|
||||
return f"Error saving message: {e!s}"
|
||||
|
||||
def get_context(
|
||||
self,
|
||||
tokens: Optional[int] = None,
|
||||
tokens: int | None = None,
|
||||
include_summary: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
|
|
@ -148,7 +163,7 @@ class HonchoTools(Toolkit):
|
|||
tokens=tokens,
|
||||
)
|
||||
|
||||
result = []
|
||||
result: list[str] = []
|
||||
|
||||
# Add summary if present
|
||||
if context.summary:
|
||||
|
|
@ -216,23 +231,31 @@ class HonchoTools(Toolkit):
|
|||
logger.exception("Error searching messages")
|
||||
return f"Error searching messages: {e!s}"
|
||||
|
||||
def query_user(self, query: str) -> str:
|
||||
def query_peer(self, query: str, target_peer_id: str | None = None) -> str:
|
||||
"""
|
||||
Query the system's knowledge about the user.
|
||||
Query the system's knowledge about a peer in the conversation.
|
||||
|
||||
Use this tool to ask questions about user preferences, interests,
|
||||
or past interactions. The system uses dialectic reasoning to
|
||||
provide insights based on the user's long-term representation.
|
||||
Use this tool to ask questions about any participant's preferences,
|
||||
interests, or past interactions. The system uses dialectic reasoning
|
||||
to provide insights based on the peer's long-term representation.
|
||||
|
||||
Args:
|
||||
query: Natural language question about the user.
|
||||
query: Natural language question about the peer.
|
||||
Examples: "What does the user like?", "What are their preferences?"
|
||||
target_peer_id: Optional peer ID to query about. If not provided,
|
||||
queries about this toolkit's own peer identity.
|
||||
|
||||
Returns:
|
||||
Response from the dialectic API with insights about the user.
|
||||
Response from the dialectic API with insights about the peer.
|
||||
"""
|
||||
try:
|
||||
response = self.user.chat(
|
||||
# Query about a specific peer, or self if not specified
|
||||
if target_peer_id:
|
||||
target = self.honcho.peer(target_peer_id)
|
||||
else:
|
||||
target = self.peer
|
||||
|
||||
response = target.chat(
|
||||
query=query,
|
||||
stream=False,
|
||||
session=self.session_id,
|
||||
|
|
@ -241,8 +264,8 @@ class HonchoTools(Toolkit):
|
|||
return str(response) if response else "No relevant information found."
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Error querying user knowledge")
|
||||
return f"Error querying user knowledge: {e!s}"
|
||||
logger.exception("Error querying peer knowledge")
|
||||
return f"Error querying peer knowledge: {e!s}"
|
||||
|
||||
def reset_session(self) -> str:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ Tests for Honcho Agno Tools
|
|||
|
||||
Tests the Agno-Honcho tool integration layer using real Honcho SDK.
|
||||
Focuses on tool interface compliance and result formatting.
|
||||
|
||||
Note: Each HonchoTools instance represents ONE agent identity (peer_id).
|
||||
Messages added via add_message() are attributed to that peer.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from honcho import Honcho
|
||||
from honcho_agno import HonchoTools
|
||||
|
||||
|
||||
|
|
@ -23,7 +25,7 @@ class TestHonchoToolsInitialization:
|
|||
assert tools.name == "honcho"
|
||||
assert tools.honcho is not None
|
||||
assert tools.session_id is not None
|
||||
assert tools.user_id == "default"
|
||||
assert tools.peer_id == "assistant"
|
||||
assert tools.app_id == "default"
|
||||
|
||||
def test_custom_initialization(self):
|
||||
|
|
@ -31,11 +33,11 @@ class TestHonchoToolsInitialization:
|
|||
custom_session = str(uuid.uuid4())
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id="test-user",
|
||||
peer_id="custom-agent",
|
||||
session_id=custom_session,
|
||||
)
|
||||
|
||||
assert tools.user_id == "test-user"
|
||||
assert tools.peer_id == "custom-agent"
|
||||
assert tools.app_id == "test-app"
|
||||
assert tools.session_id == custom_session
|
||||
|
||||
|
|
@ -43,63 +45,119 @@ class TestHonchoToolsInitialization:
|
|||
"""Test that session_id is auto-generated if not provided."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id="test-user",
|
||||
peer_id="test-agent",
|
||||
)
|
||||
|
||||
assert tools.session_id is not None
|
||||
# Should be a valid UUID format
|
||||
uuid.UUID(tools.session_id)
|
||||
|
||||
def test_toolkit_represents_single_peer(self):
|
||||
"""Test that toolkit has exactly one peer identity."""
|
||||
tools = HonchoTools(peer_id="my-agent")
|
||||
|
||||
# Should have exactly one peer
|
||||
assert tools.peer is not None
|
||||
assert tools.peer_id == "my-agent"
|
||||
# Should NOT have separate user/assistant peers
|
||||
assert not hasattr(tools, "user")
|
||||
assert not hasattr(tools, "assistant")
|
||||
|
||||
|
||||
class TestAddMessage:
|
||||
"""Tests for add_message tool."""
|
||||
|
||||
def test_add_user_message(self):
|
||||
"""Test adding a user message."""
|
||||
def test_add_message_as_peer(self):
|
||||
"""Test adding a message attributed to the toolkit's peer."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"add-msg-user-{uuid.uuid4()}",
|
||||
session_id=f"add-msg-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
result = tools.add_message("Test message content", role="user")
|
||||
|
||||
assert isinstance(result, str)
|
||||
assert "saved" in result.lower() or "success" in result.lower()
|
||||
|
||||
def test_add_assistant_message(self):
|
||||
"""Test adding an assistant message."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"add-msg-user-{uuid.uuid4()}",
|
||||
session_id=f"add-msg-session-{uuid.uuid4()}",
|
||||
)
|
||||
|
||||
result = tools.add_message("Assistant response", role="assistant")
|
||||
result = tools.add_message("Test message content")
|
||||
|
||||
assert isinstance(result, str)
|
||||
assert "saved" in result.lower() or "success" in result.lower()
|
||||
assert tools.peer_id in result # Should mention the peer
|
||||
|
||||
def test_add_multiple_messages(self):
|
||||
"""Test adding multiple messages in sequence."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"multi-msg-user-{uuid.uuid4()}",
|
||||
session_id=f"multi-msg-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
messages = [
|
||||
("Hello, I need help", "user"),
|
||||
("Of course! How can I assist?", "assistant"),
|
||||
("I want to learn Python", "user"),
|
||||
"First message from agent",
|
||||
"Second message from agent",
|
||||
"Third message from agent",
|
||||
]
|
||||
|
||||
for content, role in messages:
|
||||
result = tools.add_message(content, role=role)
|
||||
for content in messages:
|
||||
result = tools.add_message(content)
|
||||
assert isinstance(result, str)
|
||||
assert "error" not in result.lower()
|
||||
|
||||
|
||||
class TestMultiPeerConversation:
|
||||
"""Tests for multi-peer conversation patterns."""
|
||||
|
||||
def test_multiple_toolkits_same_session(self):
|
||||
"""Test multiple toolkits (agents) sharing a session."""
|
||||
session_id = f"shared-session-{uuid.uuid4().hex[:8]}"
|
||||
honcho = Honcho(workspace_id="test-app")
|
||||
|
||||
# Two agents with different identities
|
||||
agent1_tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
peer_id="agent-alpha",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
agent2_tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
peer_id="agent-beta",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
# Both add messages to the same session
|
||||
result1 = agent1_tools.add_message("Message from Alpha")
|
||||
result2 = agent2_tools.add_message("Message from Beta")
|
||||
|
||||
assert "agent-alpha" in result1
|
||||
assert "agent-beta" in result2
|
||||
assert agent1_tools.session_id == agent2_tools.session_id
|
||||
|
||||
def test_user_messages_via_honcho_directly(self):
|
||||
"""Test adding user messages via Honcho while agent uses toolkit."""
|
||||
session_id = f"mixed-session-{uuid.uuid4().hex[:8]}"
|
||||
honcho = Honcho(workspace_id="test-app")
|
||||
|
||||
# User messages added directly via Honcho
|
||||
session = honcho.session(session_id)
|
||||
user_peer = honcho.peer("user")
|
||||
session.add_messages([user_peer.message("Hello from user")])
|
||||
|
||||
# Agent uses toolkit
|
||||
agent_tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
peer_id="assistant",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
result = agent_tools.add_message("Hello from assistant")
|
||||
assert "assistant" in result
|
||||
|
||||
# Both should be in context
|
||||
context = agent_tools.get_context()
|
||||
assert isinstance(context, str)
|
||||
|
||||
|
||||
class TestGetContext:
|
||||
"""Tests for get_context tool."""
|
||||
|
||||
|
|
@ -107,8 +165,8 @@ class TestGetContext:
|
|||
"""Test getting context from empty session."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"context-user-{uuid.uuid4()}",
|
||||
session_id=f"empty-context-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"empty-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
result = tools.get_context()
|
||||
|
|
@ -119,13 +177,13 @@ class TestGetContext:
|
|||
"""Test getting context after adding messages."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"context-user-{uuid.uuid4()}",
|
||||
session_id=f"context-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"context-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
# Add messages first
|
||||
tools.add_message("I like pizza", role="user")
|
||||
tools.add_message("Great choice!", role="assistant")
|
||||
tools.add_message("I like pizza")
|
||||
tools.add_message("Great choice!")
|
||||
|
||||
result = tools.get_context()
|
||||
|
||||
|
|
@ -136,11 +194,11 @@ class TestGetContext:
|
|||
"""Test getting context with token limit."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"token-user-{uuid.uuid4()}",
|
||||
session_id=f"token-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"token-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
tools.add_message("This is a test message", role="user")
|
||||
tools.add_message("This is a test message")
|
||||
|
||||
result = tools.get_context(tokens=1000)
|
||||
|
||||
|
|
@ -150,11 +208,11 @@ class TestGetContext:
|
|||
"""Test getting context without summary."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"nosummary-user-{uuid.uuid4()}",
|
||||
session_id=f"nosummary-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"nosummary-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
tools.add_message("Test message", role="user")
|
||||
tools.add_message("Test message")
|
||||
|
||||
result = tools.get_context(include_summary=False)
|
||||
|
||||
|
|
@ -168,12 +226,12 @@ class TestSearchMessages:
|
|||
"""Test that search returns formatted results."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"search-user-{uuid.uuid4()}",
|
||||
session_id=f"search-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"search-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
# Add searchable content
|
||||
tools.add_message("I enjoy Python programming and data science", role="user")
|
||||
tools.add_message("I enjoy Python programming and data science")
|
||||
|
||||
result = tools.search_messages("programming", limit=5)
|
||||
|
||||
|
|
@ -184,13 +242,13 @@ class TestSearchMessages:
|
|||
"""Test search with custom limit."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"search-limit-user-{uuid.uuid4()}",
|
||||
session_id=f"search-limit-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"search-limit-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
# Add multiple messages
|
||||
for i in range(5):
|
||||
tools.add_message(f"Test message number {i} about coding", role="user")
|
||||
tools.add_message(f"Test message number {i} about coding")
|
||||
|
||||
result = tools.search_messages("coding", limit=3)
|
||||
|
||||
|
|
@ -200,8 +258,8 @@ class TestSearchMessages:
|
|||
"""Test search with no matching results."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"search-empty-user-{uuid.uuid4()}",
|
||||
session_id=f"search-empty-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"search-empty-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
result = tools.search_messages("xyznonexistent123abcdef", limit=5)
|
||||
|
|
@ -211,35 +269,60 @@ class TestSearchMessages:
|
|||
assert "No messages found" in result or "0 found" in result.lower()
|
||||
|
||||
|
||||
class TestQueryUser:
|
||||
"""Tests for query_user tool."""
|
||||
class TestQueryPeer:
|
||||
"""Tests for query_peer tool."""
|
||||
|
||||
def test_query_returns_response(self):
|
||||
"""Test that query returns a response string."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"query-user-{uuid.uuid4()}",
|
||||
session_id=f"query-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"query-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
# Add context first
|
||||
tools.add_message("I love hiking and outdoor activities", role="user")
|
||||
tools.add_message("I also enjoy photography", role="user")
|
||||
tools.add_message("The user loves hiking and outdoor activities")
|
||||
tools.add_message("They also enjoy photography")
|
||||
|
||||
result = tools.query_user("What does the user enjoy?")
|
||||
result = tools.query_peer("What does this person enjoy?")
|
||||
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 0
|
||||
|
||||
def test_query_without_context(self):
|
||||
"""Test query on user with minimal context."""
|
||||
"""Test query with minimal context."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"query-empty-user-{uuid.uuid4()}",
|
||||
session_id=f"query-empty-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"query-empty-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
result = tools.query_user("What are the user's preferences?")
|
||||
result = tools.query_peer("What are the user's preferences?")
|
||||
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_query_specific_peer(self):
|
||||
"""Test querying about a specific peer by ID."""
|
||||
session_id = f"query-peer-session-{uuid.uuid4().hex[:8]}"
|
||||
honcho = Honcho(workspace_id="test-app")
|
||||
|
||||
# Add user messages directly
|
||||
session = honcho.session(session_id)
|
||||
user_peer = honcho.peer("user")
|
||||
session.add_messages([
|
||||
user_peer.message("I love hiking"),
|
||||
user_peer.message("Photography is my hobby"),
|
||||
])
|
||||
|
||||
# Agent queries about the user
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
peer_id="assistant",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
|
||||
result = tools.query_peer("What are their interests?", target_peer_id="user")
|
||||
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
|
@ -251,8 +334,8 @@ class TestResetSession:
|
|||
"""Test that reset creates a new session."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"reset-user-{uuid.uuid4()}",
|
||||
session_id=f"original-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"original-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
original_session = tools.session_id
|
||||
|
|
@ -271,14 +354,12 @@ class TestToolsIntegration:
|
|||
"""Test using all tools in a realistic sequence."""
|
||||
tools = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=f"integration-user-{uuid.uuid4()}",
|
||||
session_id=f"integration-session-{uuid.uuid4()}",
|
||||
peer_id=f"agent-{uuid.uuid4().hex[:8]}",
|
||||
session_id=f"integration-session-{uuid.uuid4().hex[:8]}",
|
||||
)
|
||||
|
||||
# Add messages
|
||||
add_result = tools.add_message(
|
||||
"I'm interested in AI and machine learning", role="user"
|
||||
)
|
||||
# Add message
|
||||
add_result = tools.add_message("I'm interested in AI and machine learning")
|
||||
assert isinstance(add_result, str)
|
||||
assert "error" not in add_result.lower()
|
||||
|
||||
|
|
@ -290,33 +371,42 @@ class TestToolsIntegration:
|
|||
search_result = tools.search_messages("AI", limit=10)
|
||||
assert isinstance(search_result, str)
|
||||
|
||||
# Query user
|
||||
query_result = tools.query_user("What topics interest the user?")
|
||||
# Query peer
|
||||
query_result = tools.query_peer("What topics are mentioned?")
|
||||
assert isinstance(query_result, str)
|
||||
|
||||
def test_multiple_sessions_same_user(self):
|
||||
"""Test using multiple sessions for the same user."""
|
||||
user_id = f"multi-session-user-{uuid.uuid4()}"
|
||||
def test_multi_agent_conversation(self):
|
||||
"""Test realistic multi-agent conversation."""
|
||||
session_id = f"multi-agent-{uuid.uuid4().hex[:8]}"
|
||||
honcho = Honcho(workspace_id="test-app")
|
||||
|
||||
# First session
|
||||
tools1 = HonchoTools(
|
||||
# User peer managed directly
|
||||
session = honcho.session(session_id)
|
||||
user = honcho.peer("user")
|
||||
|
||||
# Two agent toolkits
|
||||
tech_agent = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=user_id,
|
||||
session_id=f"session-1-{uuid.uuid4()}",
|
||||
peer_id="tech-advisor",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
tools1.add_message("I like Python", role="user")
|
||||
|
||||
# Second session
|
||||
tools2 = HonchoTools(
|
||||
biz_agent = HonchoTools(
|
||||
app_id="test-app",
|
||||
user_id=user_id,
|
||||
session_id=f"session-2-{uuid.uuid4()}",
|
||||
peer_id="business-advisor",
|
||||
session_id=session_id,
|
||||
honcho_client=honcho,
|
||||
)
|
||||
tools2.add_message("I also like JavaScript", role="user")
|
||||
|
||||
# Both sessions should work independently
|
||||
context1 = tools1.get_context()
|
||||
context2 = tools2.get_context()
|
||||
# Conversation flow
|
||||
session.add_messages([user.message("I want to build a SaaS product")])
|
||||
tech_agent.add_message("Consider microservices architecture")
|
||||
biz_agent.add_message("Focus on a niche market first")
|
||||
|
||||
assert isinstance(context1, str)
|
||||
assert isinstance(context2, str)
|
||||
# Both agents can see full context
|
||||
tech_context = tech_agent.get_context()
|
||||
biz_context = biz_agent.get_context()
|
||||
|
||||
assert isinstance(tech_context, str)
|
||||
assert isinstance(biz_context, str)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "basedpyright"
|
||||
version = "1.37.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodejs-wheel-binaries" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0c/b0/fbba81ea29eed1274e965cd0445f0d6020b467ff4d3393791e4d6ae02e64/basedpyright-1.37.1.tar.gz", hash = "sha256:1f47bc6f45cbcc5d6f8619d60aa42128e4b38942f5118dcd4bc20c3466c5e02f", size = 25235384, upload-time = "2026-01-08T14:42:46.447Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/d6/6b33bb49f08d761d7c958a1e3cecfb3ffbdcf4ba6bbed65b23ab47516b75/basedpyright-1.37.1-py3-none-any.whl", hash = "sha256:caf3adfe54f51623241712f8b4367adb51ef8a8c2288e3e1ec4118319661340d", size = 12297397, upload-time = "2026-01-08T14:42:50.306Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.1.4"
|
||||
|
|
@ -166,6 +178,12 @@ dependencies = [
|
|||
{ name = "python-dotenv" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "basedpyright" },
|
||||
{ name = "pytest" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "agno", specifier = ">=1.4.0" },
|
||||
|
|
@ -174,6 +192,12 @@ requires-dist = [
|
|||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "basedpyright", specifier = ">=1.29.4" },
|
||||
{ name = "pytest", specifier = ">=8.2.2" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "honcho-ai"
|
||||
version = "1.6.0"
|
||||
|
|
@ -190,7 +214,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "honcho-core"
|
||||
version = "1.9.0"
|
||||
version = "1.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
|
|
@ -200,9 +224,9 @@ dependencies = [
|
|||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/be/4496fa3deb447e958d06490e6a18ae173c8ddb427a86a60a2bcaa9bba649/honcho_core-1.9.0.tar.gz", hash = "sha256:baaa61be3826e9fd3489037a5606c6d87e6ff6ac0c2d17139ed2153691f9bae8", size = 144184, upload-time = "2026-01-12T22:11:27.159Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c8/ea/c0949bbac5a9f20625bdb152b7da2350e89ffc15b5862cd6094b464cde14/honcho_core-1.8.0.tar.gz", hash = "sha256:ffe0840639651640722ad0ed38d193cc9402b077dac3e6726ac7be551398d952", size = 142469, upload-time = "2025-12-15T19:27:59.555Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/64/99a96c61e15e6aaa696ea67a752bdcda0472712de2d5f5a0f827f0de22d9/honcho_core-1.9.0-py3-none-any.whl", hash = "sha256:b80f1215b5f9f5e134421b7243865eea6620e66fbe39629e3d59cc688031cf90", size = 138655, upload-time = "2026-01-12T22:11:26.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/9a/5aba73353c7e70d331a21e01a931c951c5bd8688fb25e5fc318517f2adf9/honcho_core-1.8.0-py3-none-any.whl", hash = "sha256:30a44b7d421328dfac015e8a6ecbe09c89b6cac9f3a913262244e7d15698a8a8", size = 140580, upload-time = "2025-12-15T19:27:58.562Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -265,6 +289,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiter"
|
||||
version = "0.12.0"
|
||||
|
|
@ -383,6 +416,22 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodejs-wheel-binaries"
|
||||
version = "24.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/f1/73182280e2c05f49a7c2c8dbd46144efe3f74f03f798fb90da67b4a93bbf/nodejs_wheel_binaries-24.13.0.tar.gz", hash = "sha256:766aed076e900061b83d3e76ad48bfec32a035ef0d41bd09c55e832eb93ef7a4", size = 8056, upload-time = "2026-01-14T11:05:33.653Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/dc/4d7548aa74a5b446d093f03aff4fb236b570959d793f21c9c42ab6ad870a/nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:356654baa37bfd894e447e7e00268db403ea1d223863963459a0fbcaaa1d9d48", size = 55133268, upload-time = "2026-01-14T11:05:05.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/8a/8a4454d28339487240dd2232f42f1090e4a58544c581792d427f6239798c/nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:92fdef7376120e575f8b397789bafcb13bbd22a1b4d21b060d200b14910f22a5", size = 55314800, upload-time = "2026-01-14T11:05:09.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/fb/46c600fcc748bd13bc536a735f11532a003b14f5c4dfd6865f5911672175/nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3f619ac140e039ecd25f2f71d6e83ad1414017a24608531851b7c31dc140cdfd", size = 59666320, upload-time = "2026-01-14T11:05:12.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/47/d48f11fc5d1541ace5d806c62a45738a1db9ce33e85a06fe4cd3d9ce83f6/nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:dfb31ebc2c129538192ddb5bedd3d63d6de5d271437cd39ea26bf3fe229ba430", size = 60162447, upload-time = "2026-01-14T11:05:16.003Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/74/d285c579ae8157c925b577dde429543963b845e69cd006549e062d1cf5b6/nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fdd720d7b378d5bb9b2710457bbc880d4c4d1270a94f13fbe257198ac707f358", size = 61659994, upload-time = "2026-01-14T11:05:19.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/97/88b4254a2ff93ed2eaed725f77b7d3d2d8d7973bf134359ce786db894faf/nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ad6383613f3485a75b054647a09f1cd56d12380d7459184eebcf4a5d403f35c", size = 62244373, upload-time = "2026-01-14T11:05:23.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/c3/0e13a3da78f08cb58650971a6957ac7bfef84164b405176e53ab1e3584e2/nodejs_wheel_binaries-24.13.0-py2.py3-none-win_amd64.whl", hash = "sha256:605be4763e3ef427a3385a55da5a1bcf0a659aa2716eebbf23f332926d7e5f23", size = 41345528, upload-time = "2026-01-14T11:05:27.67Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/f1/0578d65b4e3dc572967fd702221ea1f42e1e60accfb6b0dd8d8f15410139/nodejs_wheel_binaries-24.13.0-py2.py3-none-win_arm64.whl", hash = "sha256:2e3431d869d6b2dbeef1d469ad0090babbdcc8baaa72c01dd3cc2c6121c96af5", size = 39054688, upload-time = "2026-01-14T11:05:30.739Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "2.15.0"
|
||||
|
|
@ -411,6 +460,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.5"
|
||||
|
|
@ -567,6 +625,24 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.2.1"
|
||||
|
|
@ -689,6 +765,60 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.1"
|
||||
|
|
|
|||
Loading…
Reference in New Issue