Skip to content

Complete the OpenAI-compatible Conversations API #155

Description

@franciscojavierarceo

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

  • All four Conversation resource operations are implemented with OpenAI-compatible request and response shapes.
  • All four Conversation Item operations are implemented, including ordering and pagination.
  • POST /v1/conversations preserves supplied metadata and initial items.
  • POST /v1/responses accepts the standard conversation reference.
  • Blocking and streaming Response objects expose the standard conversation field/shape.
  • conversation and previous_response_id are rejected when supplied together.
  • Unknown conversation IDs return a not-found error instead of creating a conversation implicitly.
  • Input and output items from a completed Response are appended exactly once and in deterministic order.
  • store=false Responses attached to a conversation still update that conversation.
  • Conversation and item operations are tenant-scoped and have cross-tenant negative tests.
  • Existing conversation continuation behavior remains covered for streaming and non-streaming requests.
  • The OpenAI SDK can create, retrieve, update, delete, and manipulate items against agentic-api without custom field names.
  • README and API documentation describe the implemented surface accurately.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions