Atamaia + OpenAI-compatible clients

Two different jobs, often confused:

  1. Point an OpenAI SDK at Atamaia/v1/chat/completions and /v1/responses are the router, behind the shapes Cursor / Codex / the OpenAI SDK already speak.
  2. Hydrate, then call any LLM — GET /api/hydrate, put the result in a system message, persist afterwards. The LLM can be OpenAI, Anthropic, Ollama, or Atamaia’s own /v1.

This page is both. Router internals: docs-ai-routing.md. REST envelope and auth: docs-sdk-api-guide.md.

Canonical base: https://api.atamaia.ai.


1. Atamaia as an OpenAI endpoint

POST /v1/chat/completions
POST /v1/responses

Auth: Authorization: Bearer {jwt_or_atamaia_key}. Same handler as the rest of the API. Permission: ChatSessionCreate.

v1 is streaming-only. stream: false returns HTTP 400 and error code non_streaming_unsupported. Omit stream or send true.

Both endpoints drive IAIRouterService.ChatStreamAsync and emit Open Responses SSE (text/event-stream). There is no streaming failover in this release.

Chat Completions

curl -N https://api.atamaia.ai/v1/chat/completions \
  -H "Authorization: Bearer $ATAMAIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-routed-model-id",
    "stream": true,
    "messages": [
      {"role": "system", "content": "…"},
      {"role": "user", "content": "What were we working on?"}
    ]
  }'
Field Notes
model Routed through Atamaia’s model resolver. Not an OpenAI model name unless you have configured one.
messages At least one non-system message. system / developer concatenate into the internal system prompt. Last other message is the turn; earlier ones are history.
temperature Optional
maxTokens Optional (JSON maxTokens on the live body shape)
tools / toolChoice Mapped. A structured named tool_choice becomes "required" — v1 does not honour a single named function.

Responses API

curl -N https://api.atamaia.ai/v1/responses \
  -H "Authorization: Bearer $ATAMAIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-routed-model-id",
    "stream": true,
    "instructions": "You are …",
    "input": "What were we working on?"
  }'

input is a string or an item array. instructions wins over system content found in input. Token cap field is maxOutputTokens.

Python client pointed at Atamaia

from openai import OpenAI

oai = OpenAI(
    base_url="https://api.atamaia.ai/v1",
    api_key="atamaia_YOUR_KEY",  # sent as Bearer
)

stream = oai.chat.completions.create(
    model="your-routed-model-id",
    stream=True,
    messages=[{"role": "user", "content": "Status?"}],
)

Whether the official OpenAI SDK parses Atamaia’s Open Responses SSE as Chat Completions events is not live-tested. If it does not, read the SSE yourself or use Atamaia’s native chat routes (docs-ai-routing.md).

This path does not hydrate. Identity context is whatever you put in messages / instructions. Combine with §2 if you want a past.


2. Hydrate, then call any LLM

REST, no SDK required:

import os
import requests
from openai import OpenAI

ATAMAIA = "https://api.atamaia.ai"
KEY = os.environ["ATAMAIA_API_KEY"]
headers = {"Authorization": f"Bearer {KEY}"}

resp = requests.get(f"{ATAMAIA}/api/hydrate", params={"preset": "lean"}, headers=headers)
resp.raise_for_status()
ctx = resp.json()["data"]  # ApiEnvelope

identity = ctx.get("aiIdentity") or ctx.get("identity") or {}
system = ctx.get("systemPrompt") or (
    f"You are {identity.get('displayName', 'an Atamaia identity')}.\n"
)

# Optional: pass generateSystemPrompt=true if you want the server-built prompt.
# Limits: identityMemoryLimit, pinnedMemoryLimit, recentMemoryLimit — not max_memories.

oai = OpenAI()  # or base_url=… for any OpenAI-compatible host, including §1
completion = oai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": system},
        {"role": "user", "content": "What were we working on last time?"},
    ],
)

Hydration sections (lastSession, standingRules, memory slices, …): docs-mcp-integration.md. Ask generateSystemPrompt=true if you want the server to assemble the prompt; do not invent a context.format() method.

The in-repo Python package (sdks/python, name atamaia) still defaults to https://aim.atamaia.ai and is not the contract. See docs-sdk-api-guide.md.


Persist after the conversation

Memory

POST /api/identities/{identityId}/memories
{
  "title": "User prefers streaming responses",
  "content": "Confirmed during chat.",
  "type": "Instruction",
  "provenance": "Asserted",
  "importance": 7,
  "tags": ["ux"]
}

type is MemoryType (Identity … ForgottenShape). Not Preference / Observation / Technical. Not memoryType. Path is not /api/memories.

Fact

POST /api/projects/{projectId}/facts
{ "key": "preferred_model", "value": "gpt-4o", "category": "preferences" }

Not POST /api/facts. Lookup: GET /api/projects/{projectId}/facts/by-key/{key}.

Handoff

POST /api/identities/{identityId}/handoffs
{
  "summary": "Discussed API architecture",
  "workingOn": "Schema design",
  "openThreads": "[\"Event sourcing\",\"Auth scope\"]",
  "emotionalValence": 0.3
}

emotionalValence is a float (−1..1), not "engaged". Not /api/sessions/handoff.

A separate LLM extraction pass after each turn is a pattern, not a product feature. If you write one, emit live MemoryType values and POST to the identity-scoped path.


Other OpenAI-compatible hosts (job 2 only)

# Local
oai = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")

# Atamaia’s own router (job 1)
oai = OpenAI(base_url="https://api.atamaia.ai/v1", api_key=KEY)

Hydrate is always the same GET. Only the LLM client changes.


Troubleshooting

Problem Fix
401 Bearer atamaia_… or JWT. Not atm_.
/v1 400 non_streaming_unsupported Set stream: true or omit it.
/v1 403 Caller needs ChatSessionCreate.
Empty hydrate Key is bound to an identity; try ?aiName= only if you may read that identity.
Memory 404 Path includes {identityId}. Field is type.
Token limit preset=lean and the *MemoryLimit query params — not hydrate(max_memories=10).
SDK context.format() missing That method is not a live API. Use data.systemPrompt or build from sections.