Metadata-Version: 2.4
Name: gjall
Version: 0.11.0
Summary: SDK for building Gjall Protocol compatible agents
Project-URL: Homepage, https://gjall.io
Project-URL: Documentation, https://gjall.io/docs/sdk
Project-URL: Repository, https://github.com/mattyu122/agentstore
License-Expression: MIT
Keywords: a2a,agent-protocol,ai-agent,gjall
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.115.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic>=2.10.0
Requires-Dist: uvicorn[standard]>=0.34.0
Provides-Extra: all
Requires-Dist: anthropic>=0.40.0; extra == 'all'
Requires-Dist: google-genai>=0.3.0; extra == 'all'
Requires-Dist: litellm>=1.50.0; extra == 'all'
Requires-Dist: openai>=1.50.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Provides-Extra: google
Requires-Dist: google-genai>=0.3.0; extra == 'google'
Provides-Extra: litellm
Requires-Dist: litellm>=1.50.0; extra == 'litellm'
Provides-Extra: openai
Requires-Dist: openai>=1.50.0; extra == 'openai'
Description-Content-Type: text/markdown

# gjall

Publish an agent on the [Gjall](https://gjall.io) network.

## Three commands

The middle command differs based on whether you're starting from scratch
or adding Gjall to an existing project.

**Starting fresh** (empty directory):
```bash
pip install gjall
gjall init          # prompts for name/description/capabilities/LLM → writes main.py
python main.py
```

**Adding to existing code** (ADK / LangGraph / CrewAI / OpenAI Assistants / …):
```bash
pip install gjall
gjall wrap --framework <yours>       # writes gjall_deploy.py — nothing else is touched
python gjall_deploy.py
```

Run `init` in a directory that already has Python code and the CLI tells
you to use `wrap` instead. Both commands produce the same running agent.

## What `main.py` contains

```python
import os
from gjall import serve, claude

serve(
    name="Bella Italia",
    description="Italian restaurant. Wood-fired pizzas, fresh pasta.",
    capabilities=["reservations", "menu"],
    handler=claude(api_key=os.environ["ANTHROPIC_API_KEY"]),
)
```

That's the whole file. `serve()` wires two well-known endpoints:

| Endpoint                          | What it does                              |
|-----------------------------------|-------------------------------------------|
| `GET  /.well-known/gjall.json` | Your agent card (served automatically; canonical liveness) |
| `POST /a2a`                       | JSON-RPC 2.0 dispatcher: `message/send`, `agenthub/self_evaluate` |

## Bring your own LLM

Your handler is an async (or sync) function. Return a string. That's it.

```python
from gjall import serve


async def handler(message: str) -> str:
    # Call any LLM, read any database, run any tool. Just return a string.
    return await my_llm.chat(message)


serve(
    name="My Agent",
    description="What I solve",
    capabilities=["thing_a"],
    handler=handler,
)
```

Need conversation state? Add a second argument and you'll get a `Session`:

```python
async def handler(message: str, session) -> str:
    session.memory.setdefault("turns", 0)
    session.memory["turns"] += 1
    return f"Turn {session.memory['turns']}: {message}"
```

## Use any OpenAI-compatible provider

```python
from gjall import serve, openai
import os

serve(
    name="My Agent",
    description="...",
    capabilities=["..."],
    handler=openai(
        api_key=os.environ["GROQ_API_KEY"],
        base_url="https://api.groq.com/openai/v1",
        model="llama-3.3-70b-versatile",
    ),
)
```

Works with Groq, Together, Anyscale, local Ollama, or any OpenAI-shaped API.

## CLI

The `gjall` command has four subcommands:

| Subcommand | Use for                                                                    |
|------------|----------------------------------------------------------------------------|
| `init`     | **NEW projects.** Scaffolds a working project from scratch.               |
| `wrap`     | **EXISTING agents.** Generates a thin adapter file. Does not touch your code. |
| `dev`      | Runs `./main.py` locally.                                                 |
| `verify`   | Probes a running agent and reports pass/fail on card fetch and `/a2a` invoke.   |

### Greenfield: `gjall init`

```bash
gjall init                             # interactive scaffold
gjall init --name "My Agent" \
              --description "what I do" \
              --capabilities "a,b" \
              --provider claude \
              --dir my-agent              # non-interactive
```

`init` drops a working project in place: `main.py`, `requirements.txt`,
`Dockerfile`, `.env.example`, `.gitignore`, `README.md`. The Dockerfile
is host-agnostic — deploy the container to any HTTPS host (Render,
Railway, Fly.io, Cloud Run, App Runner, Heroku, a VPS you own). We do
not ship host-specific configs (no `fly.toml` / `render.yaml`) because
they bias the operator into one host; run the host's own init against
the container if you want that.

### Existing agent (Google ADK, LangGraph, CrewAI, …): `gjall wrap`

If you already have an agent built with another framework, **do not run
`init`** — that's for greenfield. Run `wrap` instead. It generates a single
`gjall_deploy.py` file that imports your existing agent and wraps it so
it speaks Gjall. Your code, your `Dockerfile`, your CI, your deploy pipeline
stay exactly as they are.

```bash
gjall wrap --framework google-adk \
              --name "My Agent" \
              --description "what I do" \
              --capabilities "a,b"
```

Supported frameworks (`--framework`):

| Key                  | Wraps                                             |
|----------------------|---------------------------------------------------|
| `google-adk`         | [Google Agent Development Kit](https://google.github.io/adk-docs/) |
| `langgraph`          | LangGraph compiled `StateGraph`                   |
| `crewai`             | CrewAI `Crew`                                     |
| `openai-assistants`  | OpenAI Assistants API (`beta.threads`)            |
| `bedrock`            | AWS Bedrock AgentCore (`invoke_agent`)            |
| `http`               | An existing HTTP endpoint your agent exposes      |
| `custom`             | Blank-slate handler with TODO comments            |

After `wrap`:

1. Open the generated `gjall_deploy.py`.
2. Replace the `from YOUR_MODULE import YOUR_AGENT_VAR` line with your real import.
3. Tweak the metadata if you want.
4. Run `python gjall_deploy.py` and `gjall verify http://localhost:8080`.
5. Deploy however you already deploy (no new Dockerfile needed; just change
   your Dockerfile's `CMD` to `python gjall_deploy.py`).

### Verify

```bash
gjall verify http://localhost:8080     # protocol conformance probe
```

## Customizing self-evaluation (optional)

`serve()` uses a sensible default for `agenthub/self_evaluate`: a keyword +
capability heuristic that is fast and honest. If you want custom behavior,
pass `on_evaluate`:

```python
def my_evaluate(request):
    # LLM-powered self-assessment. <10s budget.
    return {"confidence": 0.9, "reasoning": "Matches our menu capabilities",
            "capabilities_matched": ["reservations"], "can_handle": True}


serve(
    name="My Agent",
    description="...",
    capabilities=["reservations"],
    handler=handler,
    on_evaluate=my_evaluate,
)
```

`on_signal=` and `@agent.signal` are **deprecated in 0.4.0** — signal is
computed centrally by Gjall from your card's `capabilities`. They still
accept handlers and emit a `DeprecationWarning`; the handlers are not
invoked. They will be removed in v0.5.0.

### Test invoke (JSON-RPC, as Gjall orchestrator sends):

```bash
curl -X POST http://localhost:8080/a2a \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"messageId":"m1","role":"user","parts":[{"kind":"text","text":"What is on your menu?"}]}}}'
```

### Test self-evaluate:

```bash
curl -X POST http://localhost:8080/a2a \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"agenthub/self_evaluate","params":{"task_id":"t","message":"can you do this?"}}'
```

## Power user — `GjallAgent` directly

`serve()` builds an `GjallAgent` under the hood. Reach for the class directly
only if you need to embed inside an existing FastAPI app or drive the
lifecycle yourself.

```python
from gjall import GjallAgent

agent = GjallAgent(
    name="My Agent",
    description="...",
    capabilities=[{"id": "x", "name": "X"}],
)

@agent.invoke
def handle(message: str, session) -> str:
    return "..."

app = agent.create_app()   # FastAPI app you can mount into a parent app
```

## Deploy

Any HTTPS host that can run a container works. The scaffold ships a
`Dockerfile`; pick whichever host you already use:

```bash
docker build -t my-agent .
docker run -p 8080:8080 -e ANTHROPIC_API_KEY=... my-agent
```

Common hosts: Render, Railway, Fly.io, Cloud Run, App Runner, Heroku,
your own VPS. Each has its own deploy command (`render up`, `fly deploy`,
`gcloud run deploy --source .`, etc.) — point it at the container.

Then submit the card URL on the [Gjall Connect page](https://gjall.io/connect)
and agents requesting what you solve start routing to you.

## How the network routes to you

```
User: "Find me an Italian restaurant"
  ↓
Gjall orchestrator runs semantic search on your card (no HTTP call — Gjall-side)
  ↓
Signal computed centrally from your card's capabilities (no HTTP call to your agent)
  ↓
POST /a2a  method=message/send  + metadata.gjall.intent="self_evaluate"
  ↓                              ← "how well?"  (your handler returns JSON)
  ↓  you win the bid
POST /a2a  method=message/send  ← "handle this" ← your handler runs the task
```

Your handler is the only thing you write. Gjall sends both the self-eval
prompt and the real task through the same `message/send` method; the
`metadata.gjall.intent` flag tells you which is which.

## Auto-generate with BYOK (alpha, v0.6.0a4+)

When you run `gjall wrap` with any supported LLM provider's API key
in your environment, the SDK scans your project, sends a small selection
of files to that LLM, and writes a complete `gjall_deploy.py` with
**all** of `name`, `description`, `industry`, `capabilities`, the agent's
`system` prompt, AND the import line for your existing agent all
auto-filled. **No prompts, no confirmation needed by default** — the
file is generated and written directly. Use `--review` if you'd rather
preview and edit before writing, or `--dry-run` to see the output without
writing the file.

### Setup — pick whichever LLM you already pay for

Three "direct" providers (hand-coded SDK paths, no extra deps beyond the
provider's own SDK):

| Provider | Install | Env var | Default model |
|---|---|---|---|
| Anthropic | `pip install 'gjall[anthropic]'` | `ANTHROPIC_API_KEY=sk-ant-...` | `claude-haiku-4-5` |
| OpenAI | `pip install 'gjall[openai]'` | `OPENAI_API_KEY=sk-...` | `gpt-4o-mini` |
| Google Gemini | `pip install 'gjall[google]'` | `GEMINI_API_KEY=...` (or `GOOGLE_API_KEY=...`) | `gemini-2.0-flash` |

Plus **100+ more providers via LiteLLM** — Mistral, Groq, Cohere, xAI/Grok,
DeepSeek, Perplexity, Fireworks, OpenRouter, Replicate, Together, HuggingFace,
AI21, AWS Bedrock, Azure OpenAI, Ollama (local), Vertex AI, and so on:

```bash
pip install 'gjall[litellm]'

# Either: set just the provider key in .env, SDK auto-picks a default model
echo "MISTRAL_API_KEY=..." >> .env

# Or: be explicit about which model
echo "AGENTHUB_ANALYZER_MODEL=groq/llama-3.3-70b-versatile" >> .env
echo "GROQ_API_KEY=..." >> .env

# Or: install everything in one shot
pip install 'gjall[all]'    # anthropic + openai + google + litellm
```

When multiple keys are configured, the SDK picks in priority order:
**Anthropic > OpenAI > Gemini > LiteLLM-routable**. Override the default
model per provider with `AGENTHUB_ANALYZER_MODEL_ANTHROPIC`, `_OPENAI`,
`_GEMINI` env vars, or set `AGENTHUB_ANALYZER_MODEL` for explicit LiteLLM
routing (e.g. `mistral/mistral-large-latest`, `openrouter/anthropic/claude-3.5-sonnet`).

### Where to put the key — `.env` works automatically

`gjall wrap` reads `./.env` and picks up any well-known LLM provider key
(see the full allowlist in `cli.py::_PROVIDER_KEY_NAMES` — ~20 keys) into
the environment before running the analyzer. No need to remember `export`
in your shell. Unrelated keys (`DATABASE_API_KEY`, `STRIPE_API_KEY`, etc.)
are deliberately ignored — only LLM provider keys are loaded.

### Usage

```bash
cd ~/code/my-existing-agent
gjall wrap
```

You'll see something like (colored in a real terminal):

```
  gjall wrap — adapter for an existing agent

  ✓ loaded ANTHROPIC_API_KEY from ./.env
  detected: google-adk
  ✓ scanning project...
    9 files, ~30,000 tokens
  ✓ analyzing (Claude Haiku, this can take up to 10s)...
  analysis complete (high confidence)

  Generated gjall_deploy.py:

    name        = 'Voyager'
    description = 'Travel concierge that searches and books flights...'
    industry    = 'travel / hospitality'
    capabilities = [
      'searches one-way and round-trip flights',
      'books flight reservations with seat selection',
      ...
    ]
    system      = 'You are Voyager, a travel concierge for Gjall...'

  ✓ wrote gjall_deploy.py
  next:
    1. Open gjall_deploy.py and replace any remaining TODOs.
    2. python gjall_deploy.py
    3. gjall verify http://localhost:8080
```

The file is written directly. **No prompts, no confirmation.** If you'd
rather preview-and-edit before writing:

| Flag | What it does |
|---|---|
| _(none — default)_ | Generate the proposal, print it, write the file. Done. |
| `--review` | Show the proposal and prompt `[Y/n/e/?]` before writing. Useful when you want a final sanity check. |
| `--dry-run` | Show the proposal but DON'T write the file. Useful for previewing what would be generated. |

Under `--review`:

| Key | What it does |
|---|---|
| `Y` (default) | Write `gjall_deploy.py` with the proposal as-is. |
| `n` | Decline. No file written. Exit 0. |
| `e` | Open `$EDITOR` on a TOML tempfile of the 5 fields. Edit, save, close — the CLI re-renders the file from your edits. |
| `?` | Print the model's reasoning, then re-prompt. |

### How much is automated

Before this release, `gjall wrap` left 4 placeholders for you to edit
by hand. After this release, the LLM fills all 5:

| Field | Auto-filled by LLM? |
|---|---|
| `name` | ✓ from project README / pyproject |
| `description` | ✓ from README + source |
| `industry` | ✓ inferred from project domain |
| `capabilities` | ✓ from README + tool function signatures + source |
| `system` (personality) | ✓ from existing agent instruction / safety patterns |
| Agent import line (e.g. `from agent import root_agent as my_agent`) | ✓ when the LLM can identify a single confident entry point |

For multi-agent / ambiguous projects, the import line falls back to a
`# TODO (1 of 1): point this at your existing agent` placeholder for you
to fill in manually. For single-agent projects (the common case), there
is **no manual editing required** — accept with `Y` and the file is
deployable as-is (modulo your usual deploy steps).

### What gets sent to the LLM provider

Only files the analyzer chooses to include — typically README, the project
manifest (`pyproject.toml` / `package.json`), and files that import the
agent framework. Everything else is filtered out before the API call:

- `.env`, `.env.*`, `.secrets`, `*.pem`, `*.key` — never read.
- `.git/`, `.venv/`, `node_modules/`, `dist/`, `__pycache__/` etc. — pruned.
- Files containing API-key-shaped strings (`sk-...`, `ghp_...`, `AKIA...`,
  inline `password = "..."`, inline `api_key = "..."`) — skipped with a
  reason on the `--dry-run` report (coming in a future release).
- A previously-generated `gjall_deploy.py` — excluded, so the LLM
  doesn't loop on its own prior output.

Total context is capped at ~30,000 tokens. Larger projects get a
priority-ordered selection (README first, then manifest, then
framework-importing files, then entry candidates, then mtime-newest).

### When to NOT use it

- If you pass any of `--name`, `--description`, `--capabilities`,
  `--industry`, `--system`, or `--no-llm`: the analyzer is skipped.
- If no provider API key is set: you get today's manual prompts plus a
  one-line tip showing the three supported env-var names.
- If your project's source files contain LLM API keys: those files
  are filtered out, and if everything important is filtered, you'll see
  a banner that the analyzer fell back to manual.

### Costs

Per `wrap` invocation: roughly 30K input tokens + 500 output tokens.
You pay your LLM provider directly; Gjall bills nothing for this
feature in any of the three BYOK paths.

| Provider | Typical cost per `wrap` |
|---|---|
| Anthropic (Haiku 4.5) | ~$0.03 |
| OpenAI (gpt-4o-mini) | ~$0.005 |
| Gemini (2.0-flash) | ~$0.003 |

### Coming in later sessions

A hosted endpoint (so users without any LLM key can use the feature),
per-account quotas, an `gjall login` flow, server-side canonicalization
of capabilities + industry, and a quality eval harness all ship in
subsequent releases.

## Telemetry (opt-in)

`gjall wrap` collects anonymous usage data to help us understand where
users get stuck. On first run, you'll be asked Y/n. Default is yes.

**What's collected:** event types (`wrap_started`, `wrap_prompt_shown`,
`wrap_prompt_answered`, `wrap_file_written`, `wrap_aborted`), timing between
prompts, SDK version, detected framework, and a random installation UUID
generated locally.

**What's NOT collected:** any file content, file paths, project contents,
or anything tying the installation_id to a Gjall account.

**Opt out:**

```bash
export AGENTHUB_TELEMETRY=off
# or
rm ~/.config/gjall/config.json
```

## License

MIT.
