for assorted things

Revert "migrate update-lights from marvin to pydantic-ai"

This reverts commit e83c0050f1eeb3bf7a7477fec1c07c4046a55d6a.

Changed files
+44 -56
+44 -56
update-lights
··· 1 1 #!/usr/bin/env -S uv run --script --quiet 2 2 # /// script 3 3 # requires-python = ">=3.12" 4 - # dependencies = ["pydantic-ai", "pydantic-settings", "rich"] 4 + # dependencies = ["marvin@git+https://github.com/prefecthq/marvin.git"] 5 5 # /// 6 6 """ 7 7 Make some change to my phillips hue network of lights via agent + MCP server. ··· 13 13 ``` 14 14 15 15 Details: 16 - - uses a [`pydantic-ai`](https://github.com/pydantic/pydantic-ai) agent 16 + - uses a [`marvin`](https://github.com/prefecthq/marvin) (built on [`pydantic-ai`](https://github.com/pydantic/pydantic-ai)) agent 17 17 - the agent spins up a [`fastmcp`](https://github.com/jlowin/fastmcp) MCP server that talks to my [`phue`](https://github.com/studioimaginaire/phue) bridge 18 18 - set `HUE_BRIDGE_IP` and `HUE_BRIDGE_USERNAME` in `.env` or otherwise in environment 19 - - uses `ANTHROPIC_API_KEY` and defaults to claude-sonnet-4-5, but you can set `AI_MODEL` in `.env` to use a different model 19 + - uses `OPENAI_API_KEY` by default, but you can set `AI_MODEL` in `.env` or otherwise in environment to use a different model 20 20 """ 21 21 22 + import marvin 22 23 import argparse 23 - import asyncio 24 24 from pydantic_settings import BaseSettings, SettingsConfigDict 25 25 from pydantic import Field 26 - from pydantic_ai import Agent 27 26 from pydantic_ai.mcp import MCPServerStdio 28 27 from pydantic_ai.models import KnownModelName 29 28 from rich.console import Console ··· 36 35 37 36 hue_bridge_ip: str = Field(default=...) 38 37 hue_bridge_username: str = Field(default=...) 39 - anthropic_api_key: str | None = Field(default=None) 40 38 41 - ai_model: KnownModelName = Field(default="anthropic:claude-sonnet-4-5") 39 + ai_model: KnownModelName = Field(default="gpt-4.1-mini") 42 40 43 41 44 42 settings = Settings() 45 43 console = Console() 46 44 47 - async def run_agent(): 48 - hub_mcp = MCPServerStdio( 49 - command="uvx", 50 - args=[ 51 - "smart-home@git+https://github.com/jlowin/fastmcp.git#subdirectory=examples/smart_home" 52 - ], 53 - env={ 54 - "HUE_BRIDGE_IP": settings.hue_bridge_ip, 55 - "HUE_BRIDGE_USERNAME": settings.hue_bridge_username, 56 - }, 45 + hub_mcp = MCPServerStdio( 46 + command="uvx", 47 + args=[ 48 + "smart-home@git+https://github.com/jlowin/fastmcp.git#subdirectory=examples/smart_home" 49 + ], 50 + env={ 51 + "HUE_BRIDGE_IP": settings.hue_bridge_ip, 52 + "HUE_BRIDGE_USERNAME": settings.hue_bridge_username, 53 + }, 54 + ) 55 + 56 + 57 + if __name__ == "__main__": 58 + parser = argparse.ArgumentParser(description="Send a command to the Marvin agent.") 59 + parser.add_argument( 60 + "--message", 61 + "-m", 62 + type=str, 63 + default="soft and dim - Jessica Pratt energy, all areas", 64 + help="The message to send to the agent (defaults to 'soft and dim - Jessica Pratt energy, all areas').", 57 65 ) 66 + args = parser.parse_args() 58 67 59 - agent = Agent( 60 - settings.ai_model, 68 + agent = marvin.Agent( 69 + model=settings.ai_model, 61 70 mcp_servers=[hub_mcp], 62 71 ) 63 72 ··· 69 78 ) 70 79 ) 71 80 72 - parser = argparse.ArgumentParser(description="Send a command to the lights agent.") 73 - parser.add_argument( 74 - "--message", 75 - "-m", 76 - type=str, 77 - default="soft and dim - Jessica Pratt energy, all areas", 78 - help="The message to send to the agent (defaults to 'soft and dim - Jessica Pratt energy, all areas').", 79 - ) 80 - parser.add_argument( 81 - "--once", 82 - action="store_true", 83 - help="Run once and exit instead of entering interactive mode.", 84 - ) 85 - args = parser.parse_args() 86 - 87 - console.print(f"\n[bold yellow]→[/bold yellow] {args.message}") 88 - result = await agent.run(args.message) 89 - console.print(f"[dim green]{result.output}[/dim green]") 90 - 91 - if not args.once: 81 + with marvin.Thread(): 82 + first = True 92 83 while True: 93 - try: 94 - user_input = Prompt.ask("\n[bold green]enter a message[/bold green]") 95 - console.print(f"[bold yellow]→[/bold yellow] {user_input}") 96 - result = await agent.run(user_input) 97 - console.print(f"[dim green]{result.output}[/dim green]") 98 - except KeyboardInterrupt: 99 - console.print("\n[dim red]exiting...[/dim red]") 100 - break 101 - 102 - 103 - if __name__ == "__main__": 104 - import os 105 - 106 - if settings.anthropic_api_key: 107 - os.environ["ANTHROPIC_API_KEY"] = settings.anthropic_api_key 108 - 109 - asyncio.run(run_agent()) 84 + if first: 85 + console.print(f"\n[bold yellow]→[/bold yellow] {args.message}") 86 + agent.run(str(args.message)) 87 + first = False 88 + else: 89 + try: 90 + user_input = Prompt.ask( 91 + "\n[bold green]enter a message[/bold green]" 92 + ) 93 + console.print(f"[bold yellow]→[/bold yellow] {user_input}") 94 + agent.run(str(user_input)) 95 + except KeyboardInterrupt: 96 + console.print("\n[dim red]exiting...[/dim red]") 97 + break