Initial commit

This commit is contained in:
Daniel Alves Rösel
2026-04-02 18:47:14 +02:00
committed by GitHub
commit 90ad5e0260
94 changed files with 7797 additions and 0 deletions

139
alveslib/agent.py Normal file
View File

@@ -0,0 +1,139 @@
"""
Thin async + sync wrappers over the Anthropic SDK for quick scripting and agent
patterns. Use this when you want direct API access with streaming; for full
agentic loops with file tools use `claude-agent-sdk` (pip install claude-agent-sdk).
Usage:
from alveslib.agent import ask, stream, Agent
# One-shot
reply = ask("Summarize this data: ...")
# Streaming to stdout
stream("Write a FastAPI endpoint that ...")
# Multi-turn agent
agent = Agent(system="You are an expert Python dev.")
reply = agent.chat("Generate a Celery task that processes CSV files")
follow = agent.chat("Now add error handling and retries")
"""
import os
import asyncio
from typing import Iterator, AsyncIterator
try:
import anthropic
_client: anthropic.Anthropic | None = anthropic.Anthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY")
)
_async_client: anthropic.AsyncAnthropic | None = anthropic.AsyncAnthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY")
)
except ImportError:
_client = None
_async_client = None
DEFAULT_MODEL = "claude-sonnet-4-5"
def _require_client() -> "anthropic.Anthropic":
if _client is None:
raise ImportError("pip install anthropic")
if not os.environ.get("ANTHROPIC_API_KEY"):
raise RuntimeError("ANTHROPIC_API_KEY not set")
return _client
def ask(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> str:
"""One-shot blocking request; returns full text."""
client = _require_client()
msg = client.messages.create(
model=model,
max_tokens=8096,
system=system or anthropic.NOT_GIVEN,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
def stream(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> Iterator[str]:
"""Streaming generator; yields text deltas. Print as they arrive."""
client = _require_client()
with client.messages.stream(
model=model,
max_tokens=8096,
system=system or anthropic.NOT_GIVEN,
messages=[{"role": "user", "content": prompt}],
) as s:
yield from s.text_stream
async def ask_async(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> str:
"""Async one-shot request."""
if _async_client is None:
raise ImportError("pip install anthropic")
msg = await _async_client.messages.create(
model=model,
max_tokens=8096,
system=system or anthropic.NOT_GIVEN,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
async def stream_async(
prompt: str, system: str = "", model: str = DEFAULT_MODEL
) -> AsyncIterator[str]:
"""Async streaming generator."""
if _async_client is None:
raise ImportError("pip install anthropic")
async with _async_client.messages.stream(
model=model,
max_tokens=8096,
system=system or anthropic.NOT_GIVEN,
messages=[{"role": "user", "content": prompt}],
) as s:
async for text in s.text_stream:
yield text
class Agent:
"""Stateful multi-turn conversation agent with optional system prompt."""
def __init__(self, system: str = "", model: str = DEFAULT_MODEL):
self.system = system
self.model = model
self.history: list[dict] = []
def chat(self, prompt: str) -> str:
client = _require_client()
self.history.append({"role": "user", "content": prompt})
msg = client.messages.create(
model=self.model,
max_tokens=8096,
system=self.system or anthropic.NOT_GIVEN,
messages=self.history,
)
reply = msg.content[0].text
self.history.append({"role": "assistant", "content": reply})
return reply
async def chat_async(self, prompt: str) -> str:
if _async_client is None:
raise ImportError("pip install anthropic")
self.history.append({"role": "user", "content": prompt})
msg = await _async_client.messages.create(
model=self.model,
max_tokens=8096,
system=self.system or anthropic.NOT_GIVEN,
messages=self.history,
)
reply = msg.content[0].text
self.history.append({"role": "assistant", "content": reply})
return reply
def reset(self) -> None:
self.history.clear()