feat: Add cli for interacting with Axiom agent
Browse files- cli.py +131 -0
- pyproject.toml +2 -0
- src/axiom/agent.py +1 -0
- src/axiom/config.py +0 -3
- uv.lock +68 -0
cli.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from typing import List, Dict, Optional
|
| 3 |
+
|
| 4 |
+
from src.axiom.agent import AxiomAgent
|
| 5 |
+
from src.axiom.config import settings, load_mcp_servers_from_config
|
| 6 |
+
from agents.mcp import MCPServer
|
| 7 |
+
import logging
|
| 8 |
+
|
| 9 |
+
# --- Configure Logging ---
|
| 10 |
+
httpx_logger = logging.getLogger("httpx")
|
| 11 |
+
httpx_logger.setLevel(logging.WARNING)
|
| 12 |
+
|
| 13 |
+
# Use Rich for pretty output
|
| 14 |
+
from rich.console import Console
|
| 15 |
+
from rich.markdown import Markdown
|
| 16 |
+
from rich.rule import Rule
|
| 17 |
+
from rich.text import Text
|
| 18 |
+
|
| 19 |
+
console = Console()
|
| 20 |
+
|
| 21 |
+
async def get_user_input(prompt_text: str) -> str:
|
| 22 |
+
"""Gets user input asynchronously with a styled prompt."""
|
| 23 |
+
# Render the prompt text using Rich
|
| 24 |
+
prompt_markup = Text.from_markup(prompt_text, style="bold blue")
|
| 25 |
+
return await asyncio.to_thread(console.input, prompt_markup)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
async def main():
|
| 29 |
+
console.print(Rule("[bold blue] Welcome to Axiom CLI [/bold blue]", style="blue"))
|
| 30 |
+
console.print("[dim]Type 'quit' or 'exit' to end the chat.[/dim]")
|
| 31 |
+
console.print("") # Blank line
|
| 32 |
+
|
| 33 |
+
loaded_mcp_servers: List[MCPServer] = []
|
| 34 |
+
started_mcp_servers: List[MCPServer] = []
|
| 35 |
+
agent: Optional[AxiomAgent] = None
|
| 36 |
+
chat_history: List[Dict[str, str]] = []
|
| 37 |
+
|
| 38 |
+
try:
|
| 39 |
+
# 1. Load MCP Servers
|
| 40 |
+
console.print("[bold]Loading MCP configurations...[/bold]")
|
| 41 |
+
try:
|
| 42 |
+
loaded_mcp_servers = load_mcp_servers_from_config()
|
| 43 |
+
if loaded_mcp_servers:
|
| 44 |
+
console.print(f"[green]Loaded {len(loaded_mcp_servers)} server(s) config.[/green] Attempting to start...")
|
| 45 |
+
else:
|
| 46 |
+
console.print("[yellow]No MCP server configurations found or loaded.[/yellow]")
|
| 47 |
+
|
| 48 |
+
except Exception as e:
|
| 49 |
+
console.print(f"[bold red]Error loading MCP config:[/bold red] [red]{e}[/red]")
|
| 50 |
+
|
| 51 |
+
# 2. Start MCP Servers
|
| 52 |
+
if loaded_mcp_servers:
|
| 53 |
+
for server in loaded_mcp_servers:
|
| 54 |
+
try:
|
| 55 |
+
await server.__aenter__()
|
| 56 |
+
started_mcp_servers.append(server)
|
| 57 |
+
console.print(f" [green]Started:[/green] {server.name}")
|
| 58 |
+
except Exception as e:
|
| 59 |
+
console.print(f" [yellow]Failed to start:[/yellow] {server.name} - [yellow]{e}[/yellow]")
|
| 60 |
+
|
| 61 |
+
if loaded_mcp_servers and not started_mcp_servers:
|
| 62 |
+
console.print("[yellow]Warning: All configured MCP servers failed to start. Agent will operate without MCP tools.[/yellow]")
|
| 63 |
+
elif started_mcp_servers:
|
| 64 |
+
console.print(f"[green]Successfully started {len(started_mcp_servers)} MCP server(s).[/green]")
|
| 65 |
+
|
| 66 |
+
# 3. Initialize the Agent
|
| 67 |
+
console.print("[bold]Initializing Agent...[/bold]")
|
| 68 |
+
try:
|
| 69 |
+
agent = AxiomAgent(mcp_servers=started_mcp_servers)
|
| 70 |
+
console.print(f"[bold green]{settings.AGENT_NAME} is ready![/bold green]")
|
| 71 |
+
except Exception as e:
|
| 72 |
+
console.print(f"[bold red]Fatal Error: Could not initialize Agent:[/bold red] [red]{e}[/red]")
|
| 73 |
+
raise
|
| 74 |
+
|
| 75 |
+
console.print(Rule(style="blue"))
|
| 76 |
+
|
| 77 |
+
# 4. Main chat loop
|
| 78 |
+
while True:
|
| 79 |
+
# Get user input with styled prompt
|
| 80 |
+
user_input = await get_user_input("You: ")
|
| 81 |
+
|
| 82 |
+
if user_input.lower() in ['quit', 'exit']:
|
| 83 |
+
break
|
| 84 |
+
|
| 85 |
+
chat_history.append({"role": "user", "content": user_input})
|
| 86 |
+
|
| 87 |
+
console.print("[bold green]Axiom:[/bold green]") # Print Agent prefix before response
|
| 88 |
+
|
| 89 |
+
full_response = ""
|
| 90 |
+
# Use Rich Status for the thinking indicator while streaming
|
| 91 |
+
with console.status("[bold green]Thinking...[/bold green]", spinner="dots", speed=0.5) as status:
|
| 92 |
+
try:
|
| 93 |
+
response_generator = agent.stream_agent(chat_history)
|
| 94 |
+
async for token in response_generator:
|
| 95 |
+
full_response += token
|
| 96 |
+
status.stop()
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
status.stop()
|
| 100 |
+
console.print(f"\n[bold red]Error during response:[/bold red] [red]{e}[/red]", style="red")
|
| 101 |
+
full_response = f"[Error: {e}]"
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# Render the full response using Markdown
|
| 105 |
+
if full_response:
|
| 106 |
+
console.print(Markdown(full_response)) # Renders code blocks, lists, etc.
|
| 107 |
+
else:
|
| 108 |
+
console.print("[dim](No response generated)[/dim]")
|
| 109 |
+
|
| 110 |
+
chat_history.append({"role": "assistant", "content": full_response})
|
| 111 |
+
console.print(Rule(style="blue")) # Print a rule after each turn
|
| 112 |
+
|
| 113 |
+
except Exception as e:
|
| 114 |
+
# Catch any unexpected errors from Agent init or loop setup
|
| 115 |
+
console.print(f"\n[bold red]An unhandled error occurred:[/bold red] [red]{e}[/red]", style="red", highlight=True)
|
| 116 |
+
|
| 117 |
+
finally:
|
| 118 |
+
# 5. Cleanup: Stop MCP Servers
|
| 119 |
+
if started_mcp_servers:
|
| 120 |
+
console.print(Rule("[dim]Stopping MCP servers[/dim]", style="blue"))
|
| 121 |
+
for server in started_mcp_servers:
|
| 122 |
+
try:
|
| 123 |
+
await server.__aexit__(None, None, None)
|
| 124 |
+
console.print(f" [dim]Stopped:[/dim] {server.name}")
|
| 125 |
+
except Exception as e:
|
| 126 |
+
console.print(f" [red]Error stopping:[/red] {server.name} - [red]{e}[/red]")
|
| 127 |
+
|
| 128 |
+
console.print(Rule("[bold blue] Chat ended. Goodbye! [/bold blue]", style="blue"))
|
| 129 |
+
|
| 130 |
+
if __name__ == "__main__":
|
| 131 |
+
asyncio.run(main())
|
pyproject.toml
CHANGED
|
@@ -7,7 +7,9 @@ requires-python = ">=3.11"
|
|
| 7 |
dependencies = [
|
| 8 |
"chainlit>=2.5.5",
|
| 9 |
"openai-agents>=0.0.11",
|
|
|
|
| 10 |
"python-dotenv>=1.1.0",
|
|
|
|
| 11 |
]
|
| 12 |
|
| 13 |
[tool.hatch.build.targets.wheel]
|
|
|
|
| 7 |
dependencies = [
|
| 8 |
"chainlit>=2.5.5",
|
| 9 |
"openai-agents>=0.0.11",
|
| 10 |
+
"prompt-toolkit>=3.0.51",
|
| 11 |
"python-dotenv>=1.1.0",
|
| 12 |
+
"rich>=14.0.0",
|
| 13 |
]
|
| 14 |
|
| 15 |
[tool.hatch.build.targets.wheel]
|
src/axiom/agent.py
CHANGED
|
@@ -93,6 +93,7 @@ class AxiomAgent:
|
|
| 93 |
result = Runner.run_streamed(
|
| 94 |
starting_agent=self.agent,
|
| 95 |
input=chat_history,
|
|
|
|
| 96 |
run_config=config
|
| 97 |
)
|
| 98 |
async for event in result.stream_events():
|
|
|
|
| 93 |
result = Runner.run_streamed(
|
| 94 |
starting_agent=self.agent,
|
| 95 |
input=chat_history,
|
| 96 |
+
max_turns=20,
|
| 97 |
run_config=config
|
| 98 |
)
|
| 99 |
async for event in result.stream_events():
|
src/axiom/config.py
CHANGED
|
@@ -80,8 +80,6 @@ def load_mcp_servers_from_config(config_path: Path = settings.MCP_CONFIG_PATH) -
|
|
| 80 |
logger.error(f"MCP configuration file not found: {config_path}")
|
| 81 |
raise FileNotFoundError(f"MCP configuration file not found: {config_path}")
|
| 82 |
|
| 83 |
-
logger.info(f"Loading MCP servers from: {config_path}")
|
| 84 |
-
|
| 85 |
# Allow json.JSONDecodeError to propagate if file is invalid JSON
|
| 86 |
with open(config_path, 'r', encoding='utf-8') as f:
|
| 87 |
data = json.load(f)
|
|
@@ -104,7 +102,6 @@ def load_mcp_servers_from_config(config_path: Path = settings.MCP_CONFIG_PATH) -
|
|
| 104 |
}
|
| 105 |
)
|
| 106 |
servers.append(server_instance)
|
| 107 |
-
logger.info(f"Prepared MCP Server: {name}")
|
| 108 |
|
| 109 |
except (ValidationError, Exception) as e:
|
| 110 |
logger.warning(f"Skipping MCP server '{name}' due to configuration error: {e}")
|
|
|
|
| 80 |
logger.error(f"MCP configuration file not found: {config_path}")
|
| 81 |
raise FileNotFoundError(f"MCP configuration file not found: {config_path}")
|
| 82 |
|
|
|
|
|
|
|
| 83 |
# Allow json.JSONDecodeError to propagate if file is invalid JSON
|
| 84 |
with open(config_path, 'r', encoding='utf-8') as f:
|
| 85 |
data = json.load(f)
|
|
|
|
| 102 |
}
|
| 103 |
)
|
| 104 |
servers.append(server_instance)
|
|
|
|
| 105 |
|
| 106 |
except (ValidationError, Exception) as e:
|
| 107 |
logger.warning(f"Skipping MCP server '{name}' due to configuration error: {e}")
|
uv.lock
CHANGED
|
@@ -169,14 +169,18 @@ source = { editable = "." }
|
|
| 169 |
dependencies = [
|
| 170 |
{ name = "chainlit" },
|
| 171 |
{ name = "openai-agents" },
|
|
|
|
| 172 |
{ name = "python-dotenv" },
|
|
|
|
| 173 |
]
|
| 174 |
|
| 175 |
[package.metadata]
|
| 176 |
requires-dist = [
|
| 177 |
{ name = "chainlit", specifier = ">=2.5.5" },
|
| 178 |
{ name = "openai-agents", specifier = ">=0.0.11" },
|
|
|
|
| 179 |
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
|
|
|
| 180 |
]
|
| 181 |
|
| 182 |
[[package]]
|
|
@@ -707,6 +711,18 @@ dependencies = [
|
|
| 707 |
]
|
| 708 |
sdist = { url = "https://files.pythonhosted.org/packages/7e/c1/7bd34ad0ae6cfd99512f8a40b28b9624c3b1f4e1d40c9038eabc2f870b15/literalai-0.1.201.tar.gz", hash = "sha256:29e4ccadd9d68bfea319a7f0b4fc32611b081990d9195f98e5e97a14d24d3713", size = 67832 }
|
| 709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
[[package]]
|
| 711 |
name = "markupsafe"
|
| 712 |
version = "3.0.2"
|
|
@@ -786,6 +802,15 @@ wheels = [
|
|
| 786 |
{ url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 },
|
| 787 |
]
|
| 788 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 789 |
[[package]]
|
| 790 |
name = "monotonic"
|
| 791 |
version = "1.6"
|
|
@@ -1567,6 +1592,18 @@ wheels = [
|
|
| 1567 |
{ url = "https://files.pythonhosted.org/packages/54/e2/c158366e621562ef224f132e75c1d1c1fce6b078a19f7d8060451a12d4b9/posthog-3.25.0-py2.py3-none-any.whl", hash = "sha256:85db78c13d1ecb11aed06fad53759c4e8fb3633442c2f3d0336bc0ce8a585d30", size = 89115 },
|
| 1568 |
]
|
| 1569 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1570 |
[[package]]
|
| 1571 |
name = "propcache"
|
| 1572 |
version = "0.3.1"
|
|
@@ -1748,6 +1785,15 @@ wheels = [
|
|
| 1748 |
{ url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 },
|
| 1749 |
]
|
| 1750 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1751 |
[[package]]
|
| 1752 |
name = "pyjwt"
|
| 1753 |
version = "2.10.1"
|
|
@@ -1915,6 +1961,19 @@ wheels = [
|
|
| 1915 |
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
| 1916 |
]
|
| 1917 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1918 |
[[package]]
|
| 1919 |
name = "simple-websocket"
|
| 1920 |
version = "1.1.0"
|
|
@@ -2252,6 +2311,15 @@ wheels = [
|
|
| 2252 |
{ url = "https://files.pythonhosted.org/packages/3f/82/45dddf4f5bf8b73ba27382cebb2bb3c0ee922c7ef77d936b86276aa39dca/watchfiles-0.20.0-cp37-abi3-win_arm64.whl", hash = "sha256:b17d4176c49d207865630da5b59a91779468dd3e08692fe943064da260de2c7c", size = 265344 },
|
| 2253 |
]
|
| 2254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2255 |
[[package]]
|
| 2256 |
name = "wrapt"
|
| 2257 |
version = "1.17.2"
|
|
|
|
| 169 |
dependencies = [
|
| 170 |
{ name = "chainlit" },
|
| 171 |
{ name = "openai-agents" },
|
| 172 |
+
{ name = "prompt-toolkit" },
|
| 173 |
{ name = "python-dotenv" },
|
| 174 |
+
{ name = "rich" },
|
| 175 |
]
|
| 176 |
|
| 177 |
[package.metadata]
|
| 178 |
requires-dist = [
|
| 179 |
{ name = "chainlit", specifier = ">=2.5.5" },
|
| 180 |
{ name = "openai-agents", specifier = ">=0.0.11" },
|
| 181 |
+
{ name = "prompt-toolkit", specifier = ">=3.0.51" },
|
| 182 |
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
| 183 |
+
{ name = "rich", specifier = ">=14.0.0" },
|
| 184 |
]
|
| 185 |
|
| 186 |
[[package]]
|
|
|
|
| 711 |
]
|
| 712 |
sdist = { url = "https://files.pythonhosted.org/packages/7e/c1/7bd34ad0ae6cfd99512f8a40b28b9624c3b1f4e1d40c9038eabc2f870b15/literalai-0.1.201.tar.gz", hash = "sha256:29e4ccadd9d68bfea319a7f0b4fc32611b081990d9195f98e5e97a14d24d3713", size = 67832 }
|
| 713 |
|
| 714 |
+
[[package]]
|
| 715 |
+
name = "markdown-it-py"
|
| 716 |
+
version = "3.0.0"
|
| 717 |
+
source = { registry = "https://pypi.org/simple" }
|
| 718 |
+
dependencies = [
|
| 719 |
+
{ name = "mdurl" },
|
| 720 |
+
]
|
| 721 |
+
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
| 722 |
+
wheels = [
|
| 723 |
+
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
| 724 |
+
]
|
| 725 |
+
|
| 726 |
[[package]]
|
| 727 |
name = "markupsafe"
|
| 728 |
version = "3.0.2"
|
|
|
|
| 802 |
{ url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 },
|
| 803 |
]
|
| 804 |
|
| 805 |
+
[[package]]
|
| 806 |
+
name = "mdurl"
|
| 807 |
+
version = "0.1.2"
|
| 808 |
+
source = { registry = "https://pypi.org/simple" }
|
| 809 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
| 810 |
+
wheels = [
|
| 811 |
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
| 812 |
+
]
|
| 813 |
+
|
| 814 |
[[package]]
|
| 815 |
name = "monotonic"
|
| 816 |
version = "1.6"
|
|
|
|
| 1592 |
{ url = "https://files.pythonhosted.org/packages/54/e2/c158366e621562ef224f132e75c1d1c1fce6b078a19f7d8060451a12d4b9/posthog-3.25.0-py2.py3-none-any.whl", hash = "sha256:85db78c13d1ecb11aed06fad53759c4e8fb3633442c2f3d0336bc0ce8a585d30", size = 89115 },
|
| 1593 |
]
|
| 1594 |
|
| 1595 |
+
[[package]]
|
| 1596 |
+
name = "prompt-toolkit"
|
| 1597 |
+
version = "3.0.51"
|
| 1598 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1599 |
+
dependencies = [
|
| 1600 |
+
{ name = "wcwidth" },
|
| 1601 |
+
]
|
| 1602 |
+
sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940 }
|
| 1603 |
+
wheels = [
|
| 1604 |
+
{ url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 },
|
| 1605 |
+
]
|
| 1606 |
+
|
| 1607 |
[[package]]
|
| 1608 |
name = "propcache"
|
| 1609 |
version = "0.3.1"
|
|
|
|
| 1785 |
{ url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 },
|
| 1786 |
]
|
| 1787 |
|
| 1788 |
+
[[package]]
|
| 1789 |
+
name = "pygments"
|
| 1790 |
+
version = "2.19.1"
|
| 1791 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1792 |
+
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
| 1793 |
+
wheels = [
|
| 1794 |
+
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
| 1795 |
+
]
|
| 1796 |
+
|
| 1797 |
[[package]]
|
| 1798 |
name = "pyjwt"
|
| 1799 |
version = "2.10.1"
|
|
|
|
| 1961 |
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
| 1962 |
]
|
| 1963 |
|
| 1964 |
+
[[package]]
|
| 1965 |
+
name = "rich"
|
| 1966 |
+
version = "14.0.0"
|
| 1967 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1968 |
+
dependencies = [
|
| 1969 |
+
{ name = "markdown-it-py" },
|
| 1970 |
+
{ name = "pygments" },
|
| 1971 |
+
]
|
| 1972 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 }
|
| 1973 |
+
wheels = [
|
| 1974 |
+
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
|
| 1975 |
+
]
|
| 1976 |
+
|
| 1977 |
[[package]]
|
| 1978 |
name = "simple-websocket"
|
| 1979 |
version = "1.1.0"
|
|
|
|
| 2311 |
{ url = "https://files.pythonhosted.org/packages/3f/82/45dddf4f5bf8b73ba27382cebb2bb3c0ee922c7ef77d936b86276aa39dca/watchfiles-0.20.0-cp37-abi3-win_arm64.whl", hash = "sha256:b17d4176c49d207865630da5b59a91779468dd3e08692fe943064da260de2c7c", size = 265344 },
|
| 2312 |
]
|
| 2313 |
|
| 2314 |
+
[[package]]
|
| 2315 |
+
name = "wcwidth"
|
| 2316 |
+
version = "0.2.13"
|
| 2317 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2318 |
+
sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
|
| 2319 |
+
wheels = [
|
| 2320 |
+
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
|
| 2321 |
+
]
|
| 2322 |
+
|
| 2323 |
[[package]]
|
| 2324 |
name = "wrapt"
|
| 2325 |
version = "1.17.2"
|