agentleFS
theinternetitheinterneti/TTA.dev/.github/skills/ttadev-llm/SKILL.md

ttadev-llm

Skill0 starsChanged 7 months agoMITView on GitHub

Teaches how to call an LLM using TTA.dev primitives. Use when the user wants to call an LLM, use LiteLLM, use make_resilient_llm, integrate with an LLM provider, pick a model string for groq, anthropic, openai, gemini, ollama, or openrouter, stream tokens, use tool calls, or set up Langfuse observability. Covers LiteLLMPrimitive (primary path), UniversalLLMPrimitive (fallback), LLMRequest/LLMResponse types, streaming, ToolSchema, and the primary/fallback architecture.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use llm.stream(request, ctx) — returns an AsyncIterator[str]:

    line 138
  2. Very specific:

    Every LiteLLMPrimitive.execute() call is traced.

    line 204
  3. Very specific:

    Use FallbackPrimitive to fall back from LiteLLMPrimitive to UniversalLLMPrimitive:

    line 208

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

TTA.dev LLM Primitives

TTA.dev exposes LLMs through two primitives:

PrimitiveRoleWhen to use
LiteLLMPrimitivePrimary — 100+ providers via litellmDefault for all new code
UniversalLLMPrimitiveFallback — hand-rolled provider clientsFallback / legacy; composable via FallbackPrimitive

make_resilient_llm() is the one-liner factory that composes LiteLLMPrimitive + RetryPrimitive + CachePrimitive into a production-ready stack.

When to Use This Skill

  • Making any LLM API call in TTA.dev
  • Choosing the right model string for Groq, Anthropic, Ollama, Gemini, or OpenRouter
  • Adding streaming to an LLM call
  • Using LLM tool-calling (function calling)
  • Setting up Langfuse zero-config tracing
  • Building a primary/fallback LLM architecture

Prerequisites

uv sync                        # core + litellm included
uv sync --extra groq           # Groq-specific SDK (optional — litellm handles it)
uv sync --extra anthropic      # Anthropic SDK (optional)

Environment variables:

ProviderVariable
GroqGROQ_API_KEY
AnthropicANTHROPIC_API_KEY
OpenAIOPENAI_API_KEY
Gemini (Google)GOOGLE_API_KEY
OpenRouterOPENROUTER_API_KEY
xAIXAI_API_KEY
Ollama(none — localhost:11434)
LangfuseLANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY

Model String Format

litellm uses "<provider>/<model-name>":

"groq/llama-3.1-8b-instant"
"groq/llama-3.3-70b-versatile"
"anthropic/claude-3-5-haiku-20241022"
"anthropic/claude-3-5-sonnet-20241022"
"ollama/llama3.2"
"ollama/mistral"
"gemini/gemini-2.0-flash"
"openrouter/meta-llama/llama-3-70b-instruct"

See references/provider-reference.md for the full table.

LLMRequest and LLMResponse

from ttadev.primitives.llm import LLMRequest, LLMResponse

request = LLMRequest(
    model="groq/llama-3.1-8b-instant",   # full litellm string
    messages=[{"role": "user", "content": "Hello!"}],
    system="You are a helpful assistant.",  # optional system prompt
    temperature=0.7,                        # default 0.7
    max_tokens=512,                         # optional
)

# LLMResponse fields
response: LLMResponse
response.content    # str — generated text
response.model      # str — model that responded
response.provider   # str — provider name
response.usage      # dict — {"prompt_tokens": ..., "completion_tokens": ...}
response.tool_calls # list[ToolCall] | None — when tool-calling is used

Quick Start — Single LLM Call

import asyncio
from ttadev.primitives import LiteLLMPrimitive, WorkflowContext
from ttadev.primitives.llm import LLMRequest

llm = LiteLLMPrimitive()
ctx = WorkflowContext.root("llm-demo")

response = asyncio.run(llm.execute(
    LLMRequest(
        model="groq/llama-3.1-8b-instant",
        messages=[{"role": "user", "content": "What is 2 + 2?"}],
    ),
    ctx,
))
print(response.content)   # "4"

make_resilient_llm — Production One-Liner

make_resilient_llm() composes CachePrimitive → RetryPrimitive → LiteLLMPrimitive in one call:

from ttadev.primitives import make_resilient_llm, WorkflowContext
from ttadev.primitives.llm import LLMRequest

llm = make_resilient_llm(
    "groq",                                              # provider prefix
    litellm_fallbacks=["anthropic/claude-3-haiku-20240307", "ollama/llama3.2"],
    retry_attempts=3,       # default
    cache=True,             # default — SHA-256 keyed on messages+model+params
    cache_ttl_seconds=3600, # default — 1 hour
)

ctx      = WorkflowContext.root("resilient-demo")
response = asyncio.run(llm.execute(
    LLMRequest(model="llama-3.1-8b-instant", messages=[...]),
    ctx,
))

The composed stack:

CachePrimitive       ← keyed on messages + model + params
  └─ RetryPrimitive  ← 3 attempts, exponential back-off
       └─ LiteLLMPrimitive(provider="groq", fallbacks=[...])

Streaming

Use llm.stream(request, ctx) — returns an AsyncIterator[str]:

import asyncio
from ttadev.primitives import LiteLLMPrimitive, WorkflowContext
from ttadev.primitives.llm import LLMRequest

async def stream_demo() -> None:
    llm = LiteLLMPrimitive()
    ctx = WorkflowContext.root("stream-demo")
    req = LLMRequest(
        model="groq/llama-3.1-8b-instant",
        messages=[{"role": "user", "content": "Tell me a short story."}],
    )
    async for chunk in llm.stream(req, ctx):
        print(chunk, end="", flush=True)
    print()

asyncio.run(stream_demo())

Tool Calls

Define tools with ToolSchema, pass them in LLMRequest.tools:

from ttadev.primitives import LiteLLMPrimitive, WorkflowContext
from ttadev.primitives.llm import LLMRequest, ToolSchema
import asyncio

weather_tool = ToolSchema(
    name="get_weather",
    description="Return current weather for a city.",
    parameters={
        "type": "object",
        "properties": {
            "location": {"type": "string", "description": "City name"},
        },
        "required": ["location"],
    },
)

llm = LiteLLMPrimitive()
ctx = WorkflowContext.root("tool-demo")
req = LLMRequest(
    model="groq/llama-3.3-70b-versatile",
    messages=[{"role": "user", "content": "What is the weather in Paris?"}],
    tools=[weather_tool],
    tool_choice="auto",
)
response = asyncio.run(llm.execute(req, ctx))
if response.tool_calls:
    for tc in response.tool_calls:
        print(tc.name, tc.arguments)  # get_weather {"location": "Paris"}

Langfuse Observability — Zero Config

Set env vars; tracing is automatic:

export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_PUBLIC_KEY=pk-lf-...
# LANGFUSE_HOST defaults to https://cloud.langfuse.com

Every LiteLLMPrimitive.execute() call is traced. No code changes needed.

Primary / Fallback Architecture

Use FallbackPrimitive to fall back from LiteLLMPrimitive to UniversalLLMPrimitive:

from ttadev.primitives import FallbackPrimitive, WorkflowContext
from ttadev.primitives.llm import LiteLLMPrimitive, UniversalLLMPrimitive, LLMProvider

primary  = LiteLLMPrimitive()                             # litellm — primary
fallback = UniversalLLMPrimitive(provider=LLMProvider.GROQ)  # direct SDK — fallback

resilient = FallbackPrimitive(primary, fallback)

ctx      = WorkflowContext.root("primary-fallback")
response = asyncio.run(resilient.execute(request, ctx))

Anti-Patterns

❌ Never✅ Use instead
import anthropic; client.messages.create(...) directlyLiteLLMPrimitive with model string
Hard-code retry loops around LLM callsmake_resilient_llm()
Cache responses in a global dictmake_resilient_llm(cache=True)
Hard-code a single providerlitellm_fallbacks=[...]

Provider Reference

Full table of model strings by provider → references/provider-reference.md

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.