Migration Guide
This guide covers migrating from the legacy 0.x SDKs (atheon_codex) to the new unified v1.0 architecture.
What Changed
The v1.0 release represents a fundamental shift in how Atheon integrates with your application, moving from a blocking, synchronous model to a high-performance background queue.
| Old SDKs (0.x) | New SDKs (1.x) | |
|---|---|---|
| Paradigm | Synchronous request/response | Fire-and-forget, background queue |
| Latency impact | Adds Atheon API round-trip to every response | Zero — events are batched and sent in background |
| Main call | create_atheon_unit() / createAtheonUnit() |
track(provider, model_name, input, output, ...) |
| What you get back | unit_configs (frontend config blob) |
Tuple/Array: (interaction_id, prompt_hash, fingerprint) |
| Frontend element | <atheon-container> |
<atheon-input> & <atheon-output> |
| Frontend attributes | data-atheon="{stringified configs}" |
interaction-id="{id}" prompt-hash="{hash}" fingerprint="{fp}" |
| Streaming support | ✗ | ✓ via begin() / finish() |
| Tool tracking | ✗ | ✓ via tool decorator/wrapper |
| Sub-agent tracking | ✗ | ✓ via agent decorator/wrapper |
| Init style | Class instantiation per call site | init(api_key) once at startup |
Step-by-Step Migration
1. Replace the import and initialisation
You no longer instantiate the client at every call site. You initialize it globally once at startup.
Before:
from atheon_codex import AtheonCodexClient
client = AtheonCodexClient(api_key=os.environ.get("ATHEON_API_KEY"))
After:
import atheon
# Call once at application startup
atheon.init(os.environ["ATHEON_API_KEY"])
Before:
import { AtheonCodexClient } from 'javascript-codex';
const client = new AtheonCodexClient({ apiKey: process.env.ATHEON_API_KEY });
After:
import * as atheon from '@atheon-inc/codex';
// Call once at application startup. Awaits the security handshake.
await atheon.init({ apiKey: process.env.ATHEON_API_KEY });
2. Replace create_unit with track()
This is the core change. The old call was synchronous and returned a frontend config blob. The new call enqueues the event immediately (non-blocking) and returns tracking IDs and hashes.
Before:
result = client.create_atheon_unit(
AtheonUnitCreateModel(query=user_query, base_content=llm_response)
)
return {"tracking": result.get("unit_configs", [])}
After:
interaction_id, prompt_hash, fingerprint = atheon.track(
provider="openai", # new required field
model_name="gpt-4o", # new required field
input=user_query,
output=llm_response,
tokens_input=response.usage.prompt_tokens, # optional but recommended
tokens_output=response.usage.completion_tokens,
finish_reason=response.choices.finish_reason,
)
return {
"reply": llm_response,
"interaction_id": str(interaction_id),
"prompt_hash": prompt_hash,
"fingerprint": fingerprint
}
Before:
const result = await client.createAtheonUnit({
query: userQuery,
baseContent: llmResponse
});
return { tracking: result.unitConfigs };
After:
const [interactionId, promptHash, fingerprint] = atheon.track({
provider: "openai", // new required field
modelName: "gpt-4o", // new required field
input: userQuery,
output: llmResponse,
tokensInput: response.usage.prompt_tokens,
tokensOutput: response.usage.completion_tokens,
finishReason: response.choices.finish_reason,
});
return {
reply: llmResponse,
interaction_id: interactionId,
prompt_hash: promptHash,
fingerprint: fingerprint
};
Key differences:
- query → input
- base_content → output
- provider and model_name are new required fields
- Return tuple of interaction_id, prompt_hash and fingerprint instead of unit_configs
3. Update the Async Path (Python Only)
(Note: Because Node.js is inherently asynchronous, the TS/JS SDK uses a single unified API. Python users must use the specific async methods if running within async frameworks).
Before:
from atheon_codex import AsyncAtheonCodexClient
async_client = AsyncAtheonCodexClient(api_key=os.environ.get("ATHEON_API_KEY"))
result = await async_client.create_atheon_unit(payload)
After:
import atheon
# Init once (sync, not async)
atheon.async_init(os.environ["ATHEON_API_KEY"])
# In your route handler — no await needed, enqueues immediately
interaction_id, prompt_hash, fingerprint = atheon.async_track(
provider="anthropic",
model_name="claude-sonnet-4-5",
input=user_query,
output=llm_response,
)
4. Update the Frontend
Atheon now uses two declarative web components instead of one, and relies on specific IDs rather than a stringified config blob.
Before:
<atheon-container id="chat-bubble">
<div id="content"></div>
</atheon-container>
<script>
container.setAttribute('data-atheon', JSON.stringify(backendResponse.tracking));
</script>
After:
<atheon-input>
<textarea></textarea>
<button data-atheon-submit>Send</button>
</atheon-input>
<atheon-output id="chat-bubble">
<div id="content"></div>
</atheon-output>
<script>
container.setAttribute('interaction-id', backendResponse.interaction_id);
container.setAttribute('prompt-hash', backendResponse.prompt_hash);
container.setAttribute('fingerprint', backendResponse.fingerprint);
</script>
React example:
Before:
<atheon-container data-atheon={JSON.stringify(tracking)}>
<Markdown>{llmResponse}</Markdown>
</atheon-container>
After:
<atheon-output
interaction-id={interactionId}
prompt-hash={promptHash}
fingerprint={fingerprint}
>
<Markdown>{llmResponse}</Markdown>
</atheon-output>
5. Add shutdown (new requirement)
The 0.x SDKs were stateless per-call, so no cleanup was needed. The 1.x SDKs run a background queue thread that must be flushed before your process exits.
For Django, Flask or other sync frameworks, register it with atexit:
import atexit
import atheon
atheon.init(os.environ["ATHEON_API_KEY"])
atexit.register(atheon.shutdown) # Flush on exit
For FastAPI, Starlette or other async frameworks:
from contextlib import asynccontextmanager
import atheon
from someasyncframework import Framework
@asynccontextmanager
async def lifespan(app: Framework):
atheon.async_init(os.environ["ATHEON_API_KEY"])
yield
await atheon.async_shutdown()
app = Framework(lifespan=lifespan)
import * as atheon from "@atheon-inc/codex";
async function main() {
await atheon.init({ apiKey: process.env.ATHEON_API_KEY! });
//...
await atheon.shutdown();
}
main();