Skip to content

Commit 5251b17

Browse files
feat: Add OpenAI Conversations API
Signed-off-by: Francisco Javier Arceo <[email protected]>
1 parent d15368a commit 5251b17

File tree

14 files changed

+4728
-2730
lines changed

14 files changed

+4728
-2730
lines changed

docs/_static/llama-stack-spec.html

Lines changed: 2227 additions & 1517 deletions
Large diffs are not rendered by default.

docs/_static/llama-stack-spec.yaml

Lines changed: 1780 additions & 1197 deletions
Large diffs are not rendered by default.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.
6+
7+
from .conversations import (
8+
Conversation,
9+
ConversationCreateRequest,
10+
ConversationDeletedResource,
11+
ConversationItem,
12+
ConversationItemCreateRequest,
13+
ConversationItemDeletedResource,
14+
ConversationItemList,
15+
Conversations,
16+
ConversationUpdateRequest,
17+
Metadata,
18+
)
19+
20+
__all__ = [
21+
"Conversation",
22+
"ConversationCreateRequest",
23+
"ConversationDeletedResource",
24+
"ConversationItem",
25+
"ConversationItemCreateRequest",
26+
"ConversationItemDeletedResource",
27+
"ConversationItemList",
28+
"Conversations",
29+
"ConversationUpdateRequest",
30+
"Metadata",
31+
]
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.
6+
7+
from typing import Literal, Protocol, runtime_checkable
8+
9+
from openai import NOT_GIVEN
10+
from openai._types import NotGiven
11+
from openai.types.responses.response_includable import ResponseIncludable
12+
from pydantic import BaseModel, Field
13+
14+
from llama_stack.apis.agents.openai_responses import OpenAIResponseInput
15+
from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol
16+
from llama_stack.schema_utils import json_schema_type, webmethod
17+
18+
Metadata = dict[str, str]
19+
20+
21+
@json_schema_type
22+
class ConversationCreateRequest(BaseModel):
23+
"""Request body for creating a conversation."""
24+
25+
items: list[OpenAIResponseInput] | None = Field(
26+
default=[],
27+
description="Initial items to include in the conversation context. You may add up to 20 items at a time.",
28+
max_length=20,
29+
)
30+
metadata: Metadata | None = Field(
31+
default=None,
32+
description="Set of 16 key-value pairs that can be attached to an object. Useful for storing additional information",
33+
)
34+
35+
36+
@json_schema_type
37+
class ConversationUpdateRequest(BaseModel):
38+
"""Request body for updating a conversation."""
39+
40+
metadata: Metadata = Field(
41+
...,
42+
description="Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard. Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.",
43+
)
44+
45+
46+
@json_schema_type
47+
class Conversation(BaseModel):
48+
"""Conversation object."""
49+
50+
id: str = Field(..., description="The conversation identifier")
51+
object: str = Field(default="conversation", description="Object type")
52+
created_at: int = Field(..., description="Timestamp when the conversation was created")
53+
items: list[OpenAIResponseInput] = Field(default=[], description="Items in the conversation")
54+
metadata: Metadata | None = Field(default=None, description="Conversation metadata")
55+
56+
57+
@json_schema_type
58+
class ConversationDeletedResource(BaseModel):
59+
"""Response for deleted conversation."""
60+
61+
id: str = Field(..., description="The deleted conversation identifier")
62+
object: str = Field(default="conversation.deleted", description="Object type")
63+
deleted: bool = Field(default=True, description="Whether the object was deleted")
64+
65+
66+
@json_schema_type
67+
class ConversationItem(BaseModel):
68+
"""A conversation item with metadata wrapper around OpenAIResponseInput."""
69+
70+
id: str = Field(..., description="The item identifier")
71+
object: str = Field(default="conversation.item", description="Object type")
72+
created_at: int = Field(..., description="Timestamp when the item was created")
73+
conversation_id: str = Field(..., description="The conversation this item belongs to")
74+
content: OpenAIResponseInput = Field(..., description="The actual item content")
75+
76+
77+
@json_schema_type
78+
class ConversationItemCreateRequest(BaseModel):
79+
"""Request body for creating conversation items."""
80+
81+
items: list[OpenAIResponseInput] = Field(
82+
...,
83+
description="Items to include in the conversation context. You may add up to 20 items at a time.",
84+
max_length=20,
85+
)
86+
87+
88+
@json_schema_type
89+
class ConversationItemList(BaseModel):
90+
"""List of conversation items with pagination."""
91+
92+
object: str = Field(default="list", description="Object type")
93+
data: list[OpenAIResponseInput] = Field(..., description="List of conversation items")
94+
first_id: str | None = Field(default=None, description="The ID of the first item in the list")
95+
last_id: str | None = Field(default=None, description="The ID of the last item in the list")
96+
has_more: bool = Field(default=False, description="Whether there are more items available")
97+
98+
99+
@json_schema_type
100+
class ConversationItemDeletedResource(BaseModel):
101+
"""Response for deleted conversation item."""
102+
103+
id: str = Field(..., description="The deleted item identifier")
104+
object: str = Field(default="conversation.item.deleted", description="Object type")
105+
deleted: bool = Field(default=True, description="Whether the object was deleted")
106+
107+
108+
@runtime_checkable
109+
@trace_protocol
110+
class Conversations(Protocol):
111+
"""Protocol for conversation management operations."""
112+
113+
@webmethod(route="/v1/conversations", method="POST")
114+
async def create_conversation(self, request: ConversationCreateRequest) -> Conversation:
115+
"""Create a conversation.
116+
117+
:param request: The conversation creation request
118+
:return: The created conversation object
119+
"""
120+
...
121+
122+
@webmethod(route="/v1/conversations/{conversation_id}", method="GET")
123+
async def get_conversation(self, conversation_id: str) -> Conversation:
124+
"""Get a conversation with the given ID.
125+
126+
:param conversation_id: The conversation identifier
127+
:return: The conversation object
128+
"""
129+
...
130+
131+
@webmethod(route="/v1/conversations/{conversation_id}", method="PATCH")
132+
async def update_conversation(self, conversation_id: str, request: ConversationUpdateRequest) -> Conversation:
133+
"""Update a conversation's metadata with the given ID.
134+
135+
:param conversation_id: The conversation identifier
136+
:param request: The conversation update request
137+
:return: The updated conversation object
138+
"""
139+
...
140+
141+
@webmethod(route="/v1/conversations/{conversation_id}", method="DELETE")
142+
async def delete_conversation(self, conversation_id: str) -> ConversationDeletedResource:
143+
"""Delete a conversation with the given ID.
144+
145+
:param conversation_id: The conversation identifier
146+
:return: The deleted conversation resource
147+
"""
148+
...
149+
150+
@webmethod(route="/v1/conversations/{conversation_id}/items", method="POST")
151+
async def create(self, conversation_id: str, request: ConversationItemCreateRequest) -> ConversationItemList:
152+
"""Create items in the conversation.
153+
154+
:param conversation_id: The conversation identifier
155+
:param request: The items creation request
156+
:return: List of created items
157+
"""
158+
...
159+
160+
@webmethod(route="/v1/conversations/{conversation_id}/items/{item_id}", method="GET")
161+
async def retrieve(self, conversation_id: str, item_id: str) -> ConversationItem:
162+
"""Retrieve a conversation item.
163+
164+
:param conversation_id: The conversation identifier
165+
:param item_id: The item identifier
166+
:return: The conversation item
167+
"""
168+
...
169+
170+
@webmethod(route="/v1/conversations/{conversation_id}/items", method="GET")
171+
async def list(
172+
self,
173+
conversation_id: str,
174+
after: str | NotGiven = NOT_GIVEN,
175+
include: list[ResponseIncludable] | NotGiven = NOT_GIVEN,
176+
limit: int | NotGiven = NOT_GIVEN,
177+
order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN,
178+
) -> ConversationItemList:
179+
"""List items in the conversation.
180+
181+
:param conversation_id: The conversation identifier
182+
:param after: An item ID to list items after, used in pagination
183+
:param include: Specify additional output data to include in the response
184+
:param limit: A limit on the number of objects to be returned (1-100, default 20)
185+
:param order: The order to return items in (asc or desc, default desc)
186+
:return: List of conversation items
187+
"""
188+
...
189+
190+
@webmethod(route="/v1/conversations/{conversation_id}/items/{item_id}", method="DELETE")
191+
async def delete(self, conversation_id: str, item_id: str) -> ConversationItemDeletedResource:
192+
"""Delete a conversation item.
193+
194+
:param conversation_id: The conversation identifier
195+
:param item_id: The item identifier
196+
:return: The deleted item resource
197+
"""
198+
...

llama_stack/apis/datatypes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class Api(Enum, metaclass=DynamicApiMeta):
129129
tool_groups = "tool_groups"
130130
files = "files"
131131
prompts = "prompts"
132+
conversations = "conversations"
132133

133134
# built-in API
134135
inspect = "inspect"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.

0 commit comments

Comments
 (0)