Problem statement / motivation
agentic-api already has most of the internal machinery needed for durable conversation state: ordered item persistence, response records, rehydration, automatic input/output item appends, streaming and non-streaming continuation, and conversation isolation.
The public API surface is still incomplete and is not wire-compatible with the current OpenAI Conversations API:
- Only
POST /v1/conversations is registered.
- Conversation creation ignores the standard
metadata and initial items fields and always returns empty metadata.
- Conversation retrieve, update, and delete operations are missing.
- Conversation item create, list, retrieve, and delete operations are missing.
- Responses requests use the local
conversation_id field rather than the standard conversation reference.
- A Responses request with
store=true currently calls ConversationStore::get_or_create(), allowing an unknown client-supplied conversation ID to create a new conversation implicitly.
tenant_id columns exist, but conversation and item operations are not tenant-scoped.
This means the existing stateful behavior is useful internally, but OpenAI SDK clients cannot rely on the standard Conversations resource contract. It also prevents us from cleanly treating an authorized Conversation ID as the Responses-side source of a normalized session coordinate for downstream llm-d/vLLM policy.
Relevant current code:
crates/agentic-server/src/app.rs::build_router
crates/agentic-server/src/handler/http/conversations.rs::conversations
crates/agentic-server-core/src/types/request_response.rs::RequestPayload
crates/agentic-server-core/src/executor/rehydrate.rs::rehydrate_conversation
crates/agentic-server-core/src/executor/rehydrate.rs::from_conversation
crates/agentic-server-core/src/executor/modes/conversation.rs::ConversationHandler
crates/agentic-server-core/src/storage/conversation.rs::ConversationStore
crates/agentic-server-core/src/storage/models/item.rs::create_in_tx_with_next_conversation_seq
Official API references:
Proposed solution
Complete the Conversations API while preserving the existing executor and storage boundaries.
Conversation resource
Implement the standard resource operations and request/response shapes:
POST /v1/conversations
- Parse and persist
metadata.
- Accept and persist initial
items in the supplied order.
- Remove the nonstandard requirement around a
store field.
GET /v1/conversations/{conversation_id}
POST /v1/conversations/{conversation_id} for metadata updates
DELETE /v1/conversations/{conversation_id}
Conversation items
Implement the standard item operations:
POST /v1/conversations/{conversation_id}/items
GET /v1/conversations/{conversation_id}/items
GET /v1/conversations/{conversation_id}/items/{item_id}
DELETE /v1/conversations/{conversation_id}/items/{item_id}
Match the current OpenAI schemas, ordering, pagination parameters, list envelope, object types, errors, and deletion responses. Reuse the existing items table and sequence-number allocation rather than creating a parallel item representation.
Responses integration
- Add the standard
conversation reference to the Responses request type and make it the canonical wire field.
- Preserve
conversation_id only as a temporary input alias for existing agentic-api callers; reject requests that provide both aliases with different values.
- Return the standard
conversation representation in blocking responses and streaming response objects instead of emitting conversation_id.
- Continue rejecting
conversation together with previous_response_id.
- Require a referenced conversation to exist. Do not implicitly create it from a Responses request.
- Preserve the current behavior in which the conversation's ordered items are prepended before inference and the response's new input/output items are appended after completion.
- Preserve conversation persistence when the Responses request sets
store=false; store governs stored-response behavior, not the lifetime of an explicitly referenced conversation.
Tenant and lifecycle identity
- Scope conversation, item, and response lookups and mutations by the authenticated tenant.
- Prevent cross-tenant access even when a caller knows a conversation or item ID.
- Make the authorized, tenant-scoped Conversation ID available as the Responses-side source of a normalized internal session coordinate.
- Keep that coordinate as lifecycle/routing metadata. It must not be added to vLLM KV block hashing or treated as proof that reusable blocks are resident.
Acceptance criteria
Test plan
Add HTTP and storage integration coverage for:
- Create with empty body, metadata, initial items, and mixed supported item types.
- Retrieve, metadata update, and deletion.
- Item create, list ordering/pagination, retrieve, and deletion.
- Conversation-not-found and item-not-found errors.
- Standard
conversation continuation over blocking, SSE, and Responses WebSocket transports.
conversation plus previous_response_id rejection.
store=false with an existing conversation.
- Concurrent item appends and deterministic sequence allocation on SQLite and PostgreSQL.
- Tenant isolation for every read and mutation path.
- Compatibility behavior for the temporary
conversation_id input alias.
- OpenAI SDK conformance tests using the public Conversations and Responses clients.
Existing tests in crates/agentic-server-core/tests/stateful_conversation_integration.rs should remain as regression coverage, but they should be migrated to the standard conversation field.
Out of scope
- Implementing llm-d or vLLM session-aware routing policy in this issue.
- Treating a Conversation ID as exact KV-cache or block identity.
- Adding hard cache pinning or retention guarantees.
- Changing Anthropic Messages persistence semantics.
Problem statement / motivation
agentic-apialready has most of the internal machinery needed for durable conversation state: ordered item persistence, response records, rehydration, automatic input/output item appends, streaming and non-streaming continuation, and conversation isolation.The public API surface is still incomplete and is not wire-compatible with the current OpenAI Conversations API:
POST /v1/conversationsis registered.metadataand initialitemsfields and always returns empty metadata.conversation_idfield rather than the standardconversationreference.store=truecurrently callsConversationStore::get_or_create(), allowing an unknown client-supplied conversation ID to create a new conversation implicitly.tenant_idcolumns exist, but conversation and item operations are not tenant-scoped.This means the existing stateful behavior is useful internally, but OpenAI SDK clients cannot rely on the standard Conversations resource contract. It also prevents us from cleanly treating an authorized Conversation ID as the Responses-side source of a normalized session coordinate for downstream llm-d/vLLM policy.
Relevant current code:
crates/agentic-server/src/app.rs::build_routercrates/agentic-server/src/handler/http/conversations.rs::conversationscrates/agentic-server-core/src/types/request_response.rs::RequestPayloadcrates/agentic-server-core/src/executor/rehydrate.rs::rehydrate_conversationcrates/agentic-server-core/src/executor/rehydrate.rs::from_conversationcrates/agentic-server-core/src/executor/modes/conversation.rs::ConversationHandlercrates/agentic-server-core/src/storage/conversation.rs::ConversationStorecrates/agentic-server-core/src/storage/models/item.rs::create_in_tx_with_next_conversation_seqOfficial API references:
Proposed solution
Complete the Conversations API while preserving the existing executor and storage boundaries.
Conversation resource
Implement the standard resource operations and request/response shapes:
POST /v1/conversationsmetadata.itemsin the supplied order.storefield.GET /v1/conversations/{conversation_id}POST /v1/conversations/{conversation_id}for metadata updatesDELETE /v1/conversations/{conversation_id}Conversation items
Implement the standard item operations:
POST /v1/conversations/{conversation_id}/itemsGET /v1/conversations/{conversation_id}/itemsGET /v1/conversations/{conversation_id}/items/{item_id}DELETE /v1/conversations/{conversation_id}/items/{item_id}Match the current OpenAI schemas, ordering, pagination parameters, list envelope, object types, errors, and deletion responses. Reuse the existing
itemstable and sequence-number allocation rather than creating a parallel item representation.Responses integration
conversationreference to the Responses request type and make it the canonical wire field.conversation_idonly as a temporary input alias for existing agentic-api callers; reject requests that provide both aliases with different values.conversationrepresentation in blocking responses and streaming response objects instead of emittingconversation_id.conversationtogether withprevious_response_id.store=false;storegoverns stored-response behavior, not the lifetime of an explicitly referenced conversation.Tenant and lifecycle identity
Acceptance criteria
POST /v1/conversationspreserves supplied metadata and initial items.POST /v1/responsesaccepts the standardconversationreference.conversationandprevious_response_idare rejected when supplied together.store=falseResponses attached to a conversation still update that conversation.Test plan
Add HTTP and storage integration coverage for:
conversationcontinuation over blocking, SSE, and Responses WebSocket transports.conversationplusprevious_response_idrejection.store=falsewith an existing conversation.conversation_idinput alias.Existing tests in
crates/agentic-server-core/tests/stateful_conversation_integration.rsshould remain as regression coverage, but they should be migrated to the standardconversationfield.Out of scope