Repository: julep-ai/julep
Pull request #1277: chore(agents-api): Reasoning support & misc fixes in Responses
Author: login: HamadaSalhab
Labels: name: Review effort 3/5
Created: 2025-03-19T22:37:03Z | Merged: 2025-03-20T10:53:47Z

### **PR Type**
Enhancement, Bug fix


___

### **Description**
- Introduced enhanced reasoning support for o-series models.
  - Added `Reasoning` model with configurable effort and summary options.
  - Updated response models to support new reasoning configurations.

- Improved response format handling with new JSON schema and text formats.
  - Added `ResponseFormatJsonObject`, `ResponseFormatText`, and `TextResponseFormatJsonSchema`.
  - Enhanced tool choice options with `ToolChoiceFunction` and `ToolChoiceTypes`.

- Added validation and error handling for unsupported features.
  - Prevented unsupported reasoning and tool configurations.
  - Improved error messages for invalid configurations.

- Removed outdated and redundant code in `integrations-service`.


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>6 files</summary><table>
<tr>
  <td><strong>Responses.py</strong><dd><code>Added reasoning configurations and response format models</code></dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-611553a92fd35cd032930551aa40fb3b6d2411e0c60eb4be844b209f38ef95f8">+124/-12</a></td>

</tr>

<tr>
  <td><strong>create_response.py</strong><dd><code>Added reasoning validation and tool handling improvements</code></dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-8603f745b55dc5f83fa2a6a2df3e125db811f6f2a514a8f3c17f828b88ca2b87">+48/-4</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>get_response.py</strong><dd><code>Enhanced output handling and reasoning integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-3bb57647f0d5a37028a2b5b8d6b4b1249053c0414aa617f431806870f8b8f2dd">+62/-19</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>model_converters.py</strong><dd><code>Updated response conversion to include function tool requests</code></dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-fd87178521457cc1f7ef723d950b4dcc2dfc4764ab936f89f0bf46017ce17506">+12/-8</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>models.tsp</strong><dd><code>Updated TypeSpec models for reasoning and response formats</code></dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-881498a8f37634cdc7f067664de9a94c27078b6700f1645b761506b2bec3084f">+104/-19</a></td>

</tr>

<tr>
  <td><strong>openapi-1.0.0.yaml</strong><dd><code>Updated OpenAPI schema with new reasoning and response formats</code></dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-cc33ca315e1bd4501d3b3bcf1ca4af5628c16524390d8a8031b09a17f065d285">+136/-56</a></td>

</tr>
</table></details></td></tr><tr><td><strong>Cleanup</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>Responses.py</strong><dd><code>Removed outdated response models</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/julep-ai/julep/pull/1277/files#diff-68c765d5e14cc31bfe61adf1c759fa928ed8f62fcf434c7cf9d74fd9036514d7">+0/-361</a>&nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about Qodo Merge usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


Diff:
diff --git a/agents-api/agents_api/autogen/Responses.py b/agents-api/agents_api/autogen/Responses.py
index 4d8e65b63..874c8e887 100644
--- a/agents-api/agents_api/autogen/Responses.py
+++ b/agents-api/agents_api/autogen/Responses.py
@@ -515,10 +515,21 @@ class RankingOptions(BaseModel):
 
 
 class Reasoning(BaseModel):
+    """
+    Reasoning configuration options for o-series models.
+    """
+
     model_config = ConfigDict(
         populate_by_name=True,
     )
-    effort: Literal["low", "medium", "high"] | None = None
+    effort: Literal["low", "medium", "high"]
+    """
+    Constraint effort on reasoning models.
+    """
+    generate_summary: Literal["concise", "detailed"] | None = None
+    """
+    A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or `detailed`.
+    """
 
 
 class ReasoningContent(BaseModel):
@@ -557,14 +568,6 @@ class ReasoningItem(BaseModel):
     """
 
 
-class ReasoningModel(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    effort: Literal["low", "medium", "high"] | None = None
-    summary: str | None = None
-
-
 class Refusal(BaseModel):
     model_config = ConfigDict(
         populate_by_name=True,
@@ -602,11 +605,13 @@ class Response(BaseModel):
     ]
     parallel_tool_calls: StrictBool = True
     previous_response_id: str | None = None
-    reasoning: ReasoningModel | None = None
+    reasoning: Reasoning | None = None
     store: StrictBool = True
     temperature: float = 1
-    text: Text | None = None
-    tool_choice: Literal["auto", "none"] | ToolChoice
+    text: (
+        ResponseFormatText | TextResponseFormatJsonSchema | ResponseFormatJsonObject | None
+    ) = None
+    tool_choice: ToolChoiceTypes | ToolChoiceFunction | Literal["auto", "none", "required"]
     tools: list[FunctionTool | WebSearchTool | FileSearchTool | ComputerTool]
     top_p: float = 1
     truncation: Literal["disabled", "auto"]
@@ -625,6 +630,38 @@ class ResponseError(BaseModel):
     code: str | None = None
 
 
+class ResponseFormatJsonObject(BaseModel):
+    """
+    JSON object response format. An older method of generating JSON responses.
+
+    Using `json_schema` is recommended for models that support it. Note that the
+    model will not generate JSON without a system or user message instructing it
+    to do so.
+    """
+
+    model_config = ConfigDict(
+        populate_by_name=True,
+    )
+    type: Literal["json_object"] = "json_object"
+    """
+    The type of response format being defined. Always `json_object`.
+    """
+
+
+class ResponseFormatText(BaseModel):
+    """
+    Default response format. Used to generate text responses.
+    """
+
+    model_config = ConfigDict(
+        populate_by_name=True,
+    )
+    type: Literal["text"] = "text"
+    """
+    The type of response format being defined. Always `text`.
+    """
+
+
 class ResponseUsage(BaseModel):
     model_config = ConfigDict(
         populate_by_name=True,
@@ -705,6 +742,36 @@ class Text(BaseModel):
     format: Format | None = None
 
 
+class TextResponseFormatJsonSchema(BaseModel):
+    """
+    JSON Schema response format. Used to generate structured JSON responses. Learn more about [Structured Outputs](/docs/guides/structured-outputs).
+    """
+
+    model_config = ConfigDict(
+        populate_by_name=True,
+    )
+    type: Literal["json_schema"] = "json_schema"
+    """
+    The type of response format being defined. Always `json_schema`.
+    """
+    description: str | None = None
+    """
+    A description of what the response format is for, used by the model to determine how to respond in the format.
+    """
+    name: str | None = None
+    """
+    The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
+    """
+    schema_: Annotated[dict[str, Any], Field(alias="schema")]
+    """
+    The JSON schema that defines the structure of the response.
+    """
+    strict: StrictBool = False
+    """
+    Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`. To learn more, read the [Structured Outputs guide](/docs/guides/structured-outputs).
+    """
+
+
 class ToolChoice(BaseModel):
     model_config = ConfigDict(
         populate_by_name=True,
@@ -713,6 +780,51 @@ class ToolChoice(BaseModel):
     function: Function
 
 
+class ToolChoiceFunction(BaseModel):
+    """
+    Use this option to force the model to call a specific function.
+    """
+
+    model_config = ConfigDict(
+        populate_by_name=True,
+    )
+    type: Literal["function"] = "function"
+    """
+    For function calling, the type is always `function`.
+    """
+    name: str
+    """
+    The name of the function to call.
+    """
+
+
+class ToolChoiceTypes(BaseModel):
+    """
+    Indicates that the model should use a built-in tool to generate a response.
+
+    [Learn more about built-in tools](/docs/guides/tools).
+    """
+
+    model_config = ConfigDict(
+        populate_by_name=True,
+    )
+    type: Literal[
+        "file_search",
+        "web_search_preview",
+        "computer_use_preview",
+        "web_search_preview_2025_03_11",
+    ]
+    """
+    The type of hosted tool the model should to use. Learn more about
+    [built-in tools](/docs/guides/tools).
+
+    Allowed values are:
+    - `file_search`
+    - `web_search_preview`
+    - `computer_use_preview`
+    """
+
+
 class Type(BaseModel):
     model_config = ConfigDict(
         populate_by_name=True,
diff --git a/agents-api/agents_api/routers/responses/create_response.py b/agents-api/agents_api/routers/responses/create_response.py
index 679a4f18a..a027fb2e8 100644
--- a/agents-api/agents_api/routers/responses/create_response.py
+++ b/agents-api/agents_api/routers/responses/create_response.py
@@ -2,7 +2,7 @@
 from typing import Annotated, Any
 from uuid import UUID
 
-from fastapi import Depends
+from fastapi import Depends, HTTPException
 from fastapi.background import BackgroundTasks
 from uuid_extensions import uuid7
 
@@ -12,6 +12,7 @@
     ChunkChatResponse,
     CreateEntryRequest,
     CreateResponse,
+    FunctionToolCall,
     MessageChatResponse,
     Response,
 )
@@ -75,12 +76,23 @@ async def execute_tool_calls_async(tool_call: dict[str, Any]):
     return current_messages
 
 
+def is_reasoning_model(model: str) -> bool:
+    return model in ["o1", "o1-mini", "o1-preview", "o3-mini"]
+
+
 @router.post("/responses", tags=["responses"])
 async def create_response(
     x_developer_id: Annotated[UUID, Depends(get_developer_id)],
     create_response_data: CreateResponse,
     background_tasks: BackgroundTasks,
 ) -> Response:
+    if create_response_data.tools:
+        for tool in create_response_data.tools:
+            if tool.type == "computer-preview":
+                raise HTTPException(
+                    status_code=400, detail="Computer preview is not supported yet"
+                )
+
     developer = await get_developer_data(x_developer_id)
 
     _agent, session, chat_input = await convert_create_response(
@@ -109,6 +121,10 @@ async def create_response(
     # Prepare tools for the model - pass through tools as is
     tools_list = [tool.model_dump() for tool in chat_input.tools] if chat_input.tools else []
 
+    # top_p is not supported for reasoning models
+    if is_reasoning_model(model=settings["model"]) and settings.get("top_p"):
+        settings.pop("top_p")
+
     # Use litellm for the models
     params = {
         "messages": messages,
@@ -119,6 +135,19 @@ async def create_response(
     }
     payload = {**settings, **params}
 
+    if create_response_data.reasoning:
+        if is_reasoning_model(model=payload["model"]):
+            # Enable reasoning for supported models
+            payload["reasoning_effort"] = create_response_data.reasoning.effort
+            if create_response_data.reasoning.generate_summary:
+                raise HTTPException(
+                    status_code=400, detail="Generate summary is not supported yet"
+                )
+        else:
+            raise HTTPException(
+                status_code=400, detail="Reasoning is not supported for this model"
+            )
+
     # Get initial model response
     model_response = await litellm.acompletion(**payload)
 
@@ -129,7 +158,7 @@ async def create_response(
     tool_call_requests = assistant_message.tool_calls
 
     performed_tool_calls = []
-
+    function_tool_requests: list[FunctionToolCall] = []
     # Process tool calls if present (including multiple recursive tool calls if needed)
     if tool_call_requests and tools_list:
         # Start with the original messages
@@ -149,11 +178,26 @@ async def create_response(
         while has_tool_calls and iterations < max_iterations:
             iterations += len(tool_call_requests)
 
-            performed_tool_calls.extend(tool_call_requests)
+            # Do not process original function tool calls. They will be sent to the user as is.
+            if (
+                tool_call_requests[0].type == "function"
+                and tool_call_requests[0].function.name != "web_search_preview"
+            ):
+                function_tool_requests.append(
+                    FunctionToolCall(
+                        id="fc_" + tool_call_requests[0].id,
+                        call_id=tool_call_requests[0].id,
+                        name=tool_call_requests[0].function.name,
+                        arguments=tool_call_requests[0].function.arguments,
+                        status="completed",
+                    )
+                )
+                break
 
             # Process tool calls and get updated messages
             current_messages = await process_tool_calls(current_messages, tool_call_requests)
 
+            performed_tool_calls.extend(tool_call_requests)
             # Make a follow-up call to the model with updated messages
             response_params = {
                 "messages": current_messages,
@@ -258,7 +302,6 @@ async def create_response(
 
         # jobs = [await start_adaptive_context_workflow]
         msg = "Adaptive context is not yet implemented"
-        print("Error: Adaptive context not implemented")
         raise NotImplementedError(msg)
 
     # Return the response
@@ -285,5 +328,6 @@ async def create_response(
         chat_input=chat_input,
         session_id=session_id,
         user_id=developer.id,
+        function_tool_requests=function_tool_requests,
         performed_tool_calls=performed_tool_calls,
     )
diff --git a/agents-api/agents_api/routers/responses/get_response.py b/agents-api/agents_api/routers/responses/get_response.py
index 63da8fb7e..40ee69fae 100644
--- a/agents-api/agents_api/routers/responses/get_response.py
+++ b/agents-api/agents_api/routers/responses/get_response.py
@@ -11,11 +11,18 @@
     OutputMessage,
     OutputText,
     OutputTokensDetails,
-    ReasoningModel,
-    Response,
     ResponseUsage,
     Text,
 )
+from ...autogen.Responses import (
+    ComputerToolCall,
+    FileSearchToolCall,
+    FunctionToolCall,
+    Reasoning,
+    ReasoningItem,
+    Response,
+    WebSearchToolCall,
+)
 from ...dependencies.developer_id import get_developer_id
 from ...queries.developers.get_developer import get_developer as get_developer_query
 from ...queries.entries.get_history import get_history as get_history_query
@@ -56,6 +63,55 @@ async def get_response(
 
     last_entry = last_entries[-1]
 
+    output: list[
+        OutputMessage
+        | FileSearchToolCall
+        | FunctionToolCall
+        | WebSearchToolCall
+        | ComputerToolCall
+        | ReasoningItem
+    ] = []
+
+    for entry in last_entries:
+        if entry.tool_calls:
+            for tool_call in entry.tool_calls:
+                if tool_call.type == "function":
+                    # Specific case of the mapped `web_search_preview` tool call
+                    if tool_call.function.name == "web_search_preview":
+                        output.append(
+                            WebSearchToolCall(
+                                id=tool_call.id,
+                                status="completed",
+                            )
+                        )
+                    else:
+                        # Generic case for all other `function` tool calls
+                        output.append(
+                            FunctionToolCall(
+                                id=tool_call.id,
+                                call_id=tool_call.id,  # FIXME: Check this
+                                name=tool_call.function.name,
+                                arguments=tool_call.function.arguments,
+                                status="completed",
+                            )
+                        )
+        else:
+            output.append(
+                OutputMessage(
+                    type="message",
+                    id=str(entry.id),
+                    status="completed",
+                    role=entry.role,
+                    content=[
+                        OutputText(
+                            type="output_text",
+                            text=entry.content[0].text,
+                            annotations=[],
+                        )
+                    ],
+                )
+            )
+
     return Response(
         id=str(response_id),
         object="response",
@@ -66,25 +122,12 @@ async def get_response(
         instructions=None,
         max_output_tokens=None,
         model=last_entry.model,
-        output=[
-            OutputMessage(
-                type="message",
-                id=str(entry.id),
-                status="completed",
-                role=entry.role,
-                content=[
-                    OutputText(
-                        type="output_text",
-                        text=entry.content[0].text,
-                        annotations=[],
-                    )
-                ],
-            )
-            for entry in last_entries
-        ],
+        output=output,
         parallel_tool_calls=True,
         previous_response_id=None,
-        reasoning=ReasoningModel(effort=None, summary=None),
+        reasoning=Reasoning(
+            effort=None, summary=None
+        ),  # FIXME: We are not storing reasoning. So returning None for now.
         store=True,
         temperature=1.0,
         text=Text(format=Format(type="text")),
diff --git a/agents-api/agents_api/routers/utils/model_converters.py b/agents-api/agents_api/routers/utils/model_converters.py
index 023b37cc5..aab7c6b1d 100644
--- a/agents-api/agents_api/routers/utils/model_converters.py
+++ b/agents-api/agents_api/routers/utils/model_converters.py
@@ -216,6 +216,7 @@ def convert_chat_response_to_response(
     chat_input: ChatInput,
     session_id: UUID,
     user_id: UUID,
+    function_tool_requests: list[FunctionToolCall],
     performed_tool_calls: list[ChatCompletionMessageToolCall],
 ) -> Response:
     chat_response_usage: CompletionUsage | None = chat_response.usage
@@ -264,15 +265,18 @@ def convert_chat_response_to_response(
             pass
 
     output_text = chat_response.choices[0].message.content
-    output.append(
-        OutputMessage(
-            type="message",
-            id=str(chat_response.id),
-            status="completed",
-            role="assistant",
-            content=[OutputText(text=output_text, annotations=[])],
+    if output_text:
+        output.append(
+            OutputMessage(
+                type="message",
+                id=str(chat_response.id),
+                status="completed",
+                role="assistant",
+                content=[OutputText(text=output_text, annotations=[])],
+            )
         )
-    )
+
+    output.extend(function_tool_requests)
 
     return Response(
         id=str(session_id),
diff --git a/integrations-service/integrations/autogen/Responses.py b/integrations-service/integrations/autogen/Responses.py
deleted file mode 100644
index fa196e4c8..000000000
--- a/integrations-service/integrations/autogen/Responses.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# generated by datamodel-codegen:
-#   filename:  openapi-1.0.0.yaml
-
-from __future__ import annotations
-
-from typing import Any, Literal
-
-from pydantic import BaseModel, ConfigDict, StrictBool
-
-
-class ContentPart(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: str
-
-
-class CreateResponse(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    model: str
-    input: str | list[InputItem]
-    include: (
-        list[
-            Literal[
-                "file_search_call.results",
-                "message.input_image.image_url",
-                "computer_call_output.output.image_url",
-            ]
-        ]
-        | None
-    ) = None
-    parallel_tool_calls: StrictBool = True
-    store: StrictBool = True
-    stream: StrictBool = False
-    max_tokens: int | None = None
-    temperature: float = 1
-    top_p: float = 1
-    n: int | None = None
-    stop: str | list[str] | None = None
-    presence_penalty: float | None = None
-    frequency_penalty: float | None = None
-    logit_bias: dict[str, Any] | None = None
-    user: str | None = None
-    instructions: str | None = None
-    previous_response_id: str | None = None
-    reasoning: Reasoning | None = None
-    text: Text | None = None
-    tool_choice: Literal["auto", "none"] | ToolChoice | None = None
-    tools: list[Tool] | None = None
-    truncation: Literal["disabled", "auto"] | None = None
-    metadata: dict[str, Any] | None = None
-
-
-class FileCitation(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["file_citation"] = "file_citation"
-    """
-    The type of the file citation. Always `file_citation`.
-    """
-    index: int
-    """
-    The index of the file in the list of files.
-    """
-    file_id: str
-    """
-    The ID of the file.
-    """
-
-
-class FilePath(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["file_path"] = "file_path"
-    """
-    The type of the file path. Always `file_path`.
-    """
-    file_id: str
-    """
-    The ID of the file.
-    """
-    index: int
-    """
-    The index of the file in the list of files.
-    """
-
-
-class Format(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["text", "json_object"]
-
-
-class Function(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    name: str
-
-
-class IncompleteDetails(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    reason: Literal["max_output_tokens", "content_filter"]
-
-
-class InputContentItem(ContentPart):
-    pass
-
-
-class InputItem(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    role: Literal["user", "assistant", "system", "developer"]
-    content: list[InputContentItem]
-
-
-class InputTokensDetails(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    cached_tokens: int
-
-
-class OutputItem(ContentPart):
-    pass
-
-
-class OutputTokensDetails(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    reasoning_tokens: int
-
-
-class RankingOptions(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    ranker: Literal["auto"] | None = None
-    score_threshold: float | None = None
-
-
-class Reasoning(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    effort: Literal["low", "medium", "high"] | None = None
-
-
-class ReasoningModel(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    effort: Literal["low", "medium", "high"] | None = None
-    summary: str | None = None
-
-
-class Response(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    id: str
-    object: Literal["response"] = "response"
-    created_at: int
-    status: Literal["completed", "failed", "in_progress", "incomplete"]
-    error: ResponseError | None = None
-    incomplete_details: IncompleteDetails | None = None
-    instructions: str | None = None
-    max_output_tokens: int | None = None
-    model: str
-    output: list[MessageOutputItem]
-    parallel_tool_calls: StrictBool = True
-    previous_response_id: str | None = None
-    reasoning: ReasoningModel | None = None
-    store: StrictBool = True
-    temperature: float = 1
-    text: Text | None = None
-    tool_choice: Literal["auto", "none"] | ToolChoice
-    tools: list[Tool]
-    top_p: float = 1
-    truncation: Literal["disabled", "auto"]
-    usage: ResponseUsage
-    user: str | None = None
-    metadata: dict[str, Any]
-
-
-class ResponseError(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    message: str
-    type: str
-    param: str | None = None
-    code: str | None = None
-
-
-class ResponseUsage(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    input_tokens: int
-    input_tokens_details: InputTokensDetails
-    output_tokens: int
-    output_tokens_details: OutputTokensDetails
-    total_tokens: int
-
-
-class Text(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    format: Format | None = None
-
-
-class TextContentPart(ContentPart):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["output_text"] = "output_text"
-    text: str
-    annotations: list[FileCitation | UrlCitation | FilePath]
-
-
-class TextInputContentItem(InputContentItem):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["input_text"] = "input_text"
-    text: str
-
-
-class Tool(ContentPart):
-    pass
-
-
-class ToolChoice(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["function"] = "function"
-    function: Function
-
-
-class UrlCitation(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    url: str
-    """
-    The URL of the web resource.
-    """
-    title: str
-    """
-    The title of the web resource.
-    """
-    type: Literal["url_citation"] = "url_citation"
-    """
-    The type of the URL citation. Always `url_citation`.
-    """
-    start_index: int
-    """
-    The index of the first character of the URL citation in the message.
-    """
-    end_index: int
-    """
-    The index of the last character of the URL citation in the message.
-    """
-
-
-class UserLocation(BaseModel):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["approximate"] = "approximate"
-    city: str | None = None
-    country: str | None = None
-    region: str | None = None
-    timezone: str | None = None
-
-
-class WebSearchTool(Tool):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["web_search_preview"] = "web_search_preview"
-    domains: list[str] | None = None
-    search_context_size: Literal["small", "medium", "large"] | None = None
-    user_location: UserLocation | None = None
-
-
-class FileSearchCallOutputItem(OutputItem):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["file_search_call"] = "file_search_call"
-    id: str
-    status: Literal["completed", "in_progress"]
-    queries: list[str]
-    results: Any | None = None
-
-
-class FileSearchTool(Tool):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["file_search"] = "file_search"
-    filters: Any | None = None
-    max_num_results: int | None = None
-    ranking_options: RankingOptions | None = None
-    vector_store_ids: list[str]
-
-
-class FunctionCallOutputItem(OutputItem):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["function_call"] = "function_call"
-    id: str
-    call_id: str
-    name: str
-    arguments: str
-    status: Literal["completed", "in_progress"]
-    output: str
-
-
-class FunctionTool(Tool):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["function"] = "function"
-    name: str
-    description: str | None = None
-    parameters: dict[str, Any]
-    strict: StrictBool | None = None
-
-
-class ImageInputContentItem(InputContentItem):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["input_image"] = "input_image"
-    image_url: str
-
-
-class MessageOutputItem(OutputItem):
-    model_config = ConfigDict(
-        populate_by_name=True,
-    )
-    type: Literal["message"] = "message"
-    id: str
-    status: Literal["completed", "in_progress"]
-    role: Literal["assistant"] = "assistant"
-    content: list[TextContentPart]
diff --git a/typespec/responses/models.tsp b/typespec/responses/models.tsp
index c1e074c71..54103c411 100644
--- a/typespec/responses/models.tsp
+++ b/typespec/responses/models.tsp
@@ -48,9 +48,7 @@ model CreateResponse {
   user?: string;
   instructions?: string;
   previous_response_id?: string;
-  reasoning?: {
-    effort?: "low" | "medium" | "high";
-  };
+  reasoning?: Reasoning;
   text?: {
     format?: {
       type: "text" | "json_object";
@@ -67,12 +65,111 @@ model CreateResponse {
   metadata?: Record<unknown>;
 }
 
+alias TextResponseFormatConfiguration = ResponseFormatText | TextResponseFormatJsonSchema | ResponseFormatJsonObject;
+
+
+@doc("""
+JSON object response format. An older method of generating JSON responses.
+
+Using `json_schema` is recommended for models that support it. Note that the
+model will not generate JSON without a system or user message instructing it
+to do so.
+""")
+model ResponseFormatJsonObject {
+  @doc("The type of response format being defined. Always `json_object`.")
+  type: "json_object";
+}
+
+
+alias ResponseFormatJsonSchemaSchema = Record<unknown>;
+
+
+@doc("JSON Schema response format. Used to generate structured JSON responses. Learn more about [Structured Outputs](/docs/guides/structured-outputs).")
+model TextResponseFormatJsonSchema {
+  @doc("The type of response format being defined. Always `json_schema`.")
+  type: "json_schema";
+
+  @doc("A description of what the response format is for, used by the model to determine how to respond in the format.")
+  description?: string;
+
+  @doc("The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.")
+  name?: string;
+
+  @doc("The JSON schema that defines the structure of the response.")
+  schema: ResponseFormatJsonSchemaSchema;
+
+  @doc("Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`. To learn more, read the [Structured Outputs guide](/docs/guides/structured-outputs).")
+  strict?: boolean = false;
+}
+
+
+@doc("Default response format. Used to generate text responses.")
+model ResponseFormatText {
+  @doc("The type of response format being defined. Always `text`.")
+  type: "text";
+}
+
+
+@doc("Constraint effort on reasoning models.")
+enum ReasoningEffort {
+  low,
+  medium,
+  high,
+}
+
+@doc("Reasoning configuration options for o-series models.")
+model Reasoning {
+  effort: ReasoningEffort;
+  @doc("A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or `detailed`.")
+  generate_summary?: "concise" | "detailed";
+}
+
+enum ToolChoiceOptions {
+  auto,
+  none,
+  required,
+}
+
+@doc("""
+Indicates that the model should use a built-in tool to generate a response.
+
+[Learn more about built-in tools](/docs/guides/tools).
+""")
+model ToolChoiceTypes {
+  @doc("""
+  The type of hosted tool the model should to use. Learn more about
+  [built-in tools](/docs/guides/tools).
+
+  Allowed values are:
+  - `file_search`
+  - `web_search_preview` 
+  - `computer_use_preview`
+  """)
+  type: "file_search" | "web_search_preview" | "computer_use_preview" | "web_search_preview_2025_03_11";
+}
+
+@doc("Use this option to force the model to call a specific function.")
+model ToolChoiceFunction {
+  @doc("For function calling, the type is always `function`.")
+  type: "function";
+
+  @doc("The name of the function to call.")
+  name: string;
+}
+
+enum ResponseStatus {
+  completed,
+  failed,
+  in_progress,
+  incomplete,
+}
+
 // Model for response
 model Response {
   id: string;
   object: "response";
   created_at: int32;
-  status: "completed" | "failed" | "in_progress" | "incomplete";
+  status: ResponseStatus;
   error?: ResponseError;
   incomplete_details?: {
     reason: "max_output_tokens" | "content_filter";
@@ -83,23 +180,11 @@ model Response {
   output: OutputItem[];
   parallel_tool_calls: boolean = true;
   previous_response_id?: string;
-  reasoning?: {
-    effort?: "low" | "medium" | "high";
-    summary?: string;
-  };
+  reasoning?: Reasoning;
   store?: boolean = true;
   temperature: float = 1.0;
-  text?: {
-    format?: {
-      type: "text" | "json_object";
-    };
-  };
-  tool_choice: "auto" | "none" | {
-    type: "function";
-    function: {
-      name: string;
-    };
-  };
+  text?: TextResponseFormatConfiguration;
+  tool_choice: ToolChoiceOptions | ToolChoiceTypes | ToolChoiceFunction;
   tools: Tool[];
   top_p: float = 1.0;
   truncation: "disabled" | "auto";
diff --git a/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml b/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml
index 5c594cbfb..c99829125 100644
--- a/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml
+++ b/typespec/tsp-output/@typespec/openapi3/openapi-1.0.0.yaml
@@ -4287,14 +4287,7 @@ components:
         previous_response_id:
           type: string
         reasoning:
-          type: object
-          properties:
-            effort:
-              type: string
-              enum:
-                - low
-                - medium
-                - high
+          $ref: '#/components/schemas/Responses.Reasoning'
         text:
           type: object
           properties:
@@ -4781,6 +4774,20 @@ components:
               - $ref: '#/components/schemas/Responses.UrlCitation'
               - $ref: '#/components/schemas/Responses.FilePath'
           description: The annotations of the text output.
+    Responses.Reasoning:
+      type: object
+      required:
+        - effort
+      properties:
+        effort:
+          $ref: '#/components/schemas/Responses.ReasoningEffort'
+        generate_summary:
+          type: string
+          enum:
+            - concise
+            - detailed
+          description: A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or `detailed`.
+      description: Reasoning configuration options for o-series models.
     Responses.ReasoningContent:
       type: object
       required:
@@ -4795,6 +4802,13 @@ components:
         text:
           type: string
           description: A short summary of the reasoning used by the model when generating the response.
+    Responses.ReasoningEffort:
+      type: string
+      enum:
+        - low
+        - medium
+        - high
+      description: Constraint effort on reasoning models.
     Responses.ReasoningItem:
       type: object
       required:
@@ -4864,12 +4878,7 @@ components:
           type: integer
           format: int32
         status:
-          type: string
-          enum:
-            - completed
-            - failed
-            - in_progress
-            - incomplete
+          $ref: '#/components/schemas/Responses.ResponseStatus'
         error:
           $ref: '#/components/schemas/Responses.ResponseError'
         incomplete_details:
@@ -4899,16 +4908,7 @@ components:
         previous_response_id:
           type: string
         reasoning:
-          type: object
-          properties:
-            effort:
-              type: string
-              enum:
-                - low
-                - medium
-                - high
-            summary:
-              type: string
+          $ref: '#/components/schemas/Responses.Reasoning'
         store:
           type: boolean
           default: true
@@ -4916,40 +4916,15 @@ components:
           type: number
           default: 1
         text:
-          type: object
-          properties:
-            format:
-              type: object
-              properties:
-                type:
-                  type: string
-                  enum:
-                    - text
-                    - json_object
-              required:
-                - type
+          anyOf:
+            - $ref: '#/components/schemas/Responses.ResponseFormatText'
+            - $ref: '#/components/schemas/Responses.TextResponseFormatJsonSchema'
+            - $ref: '#/components/schemas/Responses.ResponseFormatJsonObject'
         tool_choice:
           anyOf:
-            - type: string
-              enum:
-                - auto
-                - none
-            - type: object
-              properties:
-                type:
-                  type: string
-                  enum:
-                    - function
-                function:
-                  type: object
-                  properties:
-                    name:
-                      type: string
-                  required:
-                    - name
-              required:
-                - type
-                - function
+            - $ref: '#/components/schemas/Responses.ToolChoiceOptions'
+            - $ref: '#/components/schemas/Responses.ToolChoiceTypes'
+            - $ref: '#/components/schemas/Responses.ToolChoiceFunction'
         tools:
           type: array
           items:
@@ -4983,6 +4958,40 @@ components:
           type: string
         code:
           type: string
+    Responses.ResponseFormatJsonObject:
+      type: object
+      required:
+        - type
+      properties:
+        type:
+          type: string
+          enum:
+            - json_object
+          description: The type of response format being defined. Always `json_object`.
+      description: |-
+        JSON object response format. An older method of generating JSON responses.
+
+        Using `json_schema` is recommended for models that support it. Note that the
+        model will not generate JSON without a system or user message instructing it
+        to do so.
+    Responses.ResponseFormatText:
+      type: object
+      required:
+        - type
+      properties:
+        type:
+          type: string
+          enum:
+            - text
+          description: The type of response format being defined. Always `text`.
+      description: Default response format. Used to generate text responses.
+    Responses.ResponseStatus:
+      type: string
+      enum:
+        - completed
+        - failed
+        - in_progress
+        - incomplete
     Responses.ResponseUsage:
       type: object
       required:
@@ -5059,12 +5068,83 @@ components:
           type: integer
           format: int32
           description: The vertical scroll distance.
+    Responses.TextResponseFormatJsonSchema:
+      type: object
+      required:
+        - type
+        - schema
+      properties:
+        type:
+          type: string
+          enum:
+            - json_schema
+          description: The type of response format being defined. Always `json_schema`.
+        description:
+          type: string
+          description: A description of what the response format is for, used by the model to determine how to respond in the format.
+        name:
+          type: string
+          description: The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
+        schema:
+          type: object
+          additionalProperties: {}
+          description: The JSON schema that defines the structure of the response.
+        strict:
+          type: boolean
+          description: Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`. To learn more, read the [Structured Outputs guide](/docs/guides/structured-outputs).
+          default: false
+      description: JSON Schema response format. Used to generate structured JSON responses. Learn more about [Structured Outputs](/docs/guides/structured-outputs).
     Responses.Tool:
       anyOf:
         - $ref: '#/components/schemas/Responses.FunctionTool'
         - $ref: '#/components/schemas/Responses.WebSearchTool'
         - $ref: '#/components/schemas/Responses.FileSearchTool'
         - $ref: '#/components/schemas/Responses.ComputerTool'
+    Responses.ToolChoiceFunction:
+      type: object
+      required:
+        - type
+        - name
+      properties:
+        type:
+          type: string
+          enum:
+            - function
+          description: For function calling, the type is always `function`.
+        name:
+          type: string
+          description: The name of the function to call.
+      description: Use this option to force the model to call a specific function.
+    Responses.ToolChoiceOptions:
+      type: string
+      enum:
+        - auto
+        - none
+        - required
+    Responses.ToolChoiceTypes:
+      type: object
+      required:
+        - type
+      properties:
+        type:
+          type: string
+          enum:
+            - file_search
+            - web_search_preview
+            - computer_use_preview
+            - web_search_preview_2025_03_11
+          description: |-
+            The type of hosted tool the model should to use. Learn more about
+            [built-in tools](/docs/guides/tools).
+
+            Allowed values are:
+            - `file_search`
+            - `web_search_preview` 
+            - `computer_use_preview`
+      description: |-
+        Indicates that the model should use a built-in tool to generate a response.
+
+        [Learn more about built-in tools](/docs/guides/tools).
     Responses.Type:
       type: object
       required:


Reviews:
user: login: entelligence-ai-pr-reviews[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:38:46Z
user: login: entelligence-ai-pr-reviews[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:38:49Z
user: login: ellipsis-dev[bot]
body: :x: Changes requested. Reviewed everything up to 9dc1cc0221b92a06ffcfe7364bf9a89a01c89f7c in <ellipsistime>3 minutes and 35 seconds</ellipsistime>

<details>
<summary>More details</summary>

 - Looked at `1247` lines of code in `7` files
 - Skipped `0` files when reviewing.
 - Skipped posting `17` drafted comments based on config settings.


<details>
<summary>1. <code>agents-api/agents_api/autogen/Responses.py:602</code></summary>

- **Draft comment:** 
The ReasoningModel class is removed and replaced by the updated Reasoning. Ensure that clients depending on the old model are updated since this change might break backwards compatibility.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>2. <code>agents-api/agents_api/autogen/Responses.py:615</code></summary>

- **Draft comment:** 
The 'text' field type in Response now becomes a union of several models. Verify that this change aligns with client expectations and that serialization/deserialization works as intended.
- **Reason this comment was not posted:** 
Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%`
The comment asks the PR author to verify alignment with client expectations and serialization/deserialization, which is against the rules. It doesn't provide a specific suggestion or ask for a specific test to be written.

</details>

<details>
<summary>3. <code>agents-api/agents_api/autogen/Responses.py:620</code></summary>

- **Draft comment:** 
The 'tool_choice' field type in Response has been modified to include additional union types. Confirm that the logic handling tool choice in other parts of the code correctly distinguishes these types.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>4. <code>agents-api/agents_api/routers/responses/create_response.py:76</code></summary>

- **Draft comment:** 
The addition of a check for tool type 'computer-preview' is good; ensure that all supported tool types are documented and that this exception aligns with pending support plans.
- **Reason this comment was not posted:** 
Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%`
The comment is asking the author to ensure documentation and alignment with support plans, which is similar to asking for confirmation or verification. This violates the rule against asking the author to ensure or verify intentions or plans.

</details>

<details>
<summary>5. <code>agents-api/agents_api/routers/responses/create_response.py:121</code></summary>

- **Draft comment:** 
When top_p is provided for a reasoning model, it’s being popped from the settings. Verify that this removal is intentional and that no unexpected behavior arises from top_p being silently dropped.
- **Reason this comment was not posted:** 
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50%
The comment is asking for verification of intended behavior, which violates our rules about not asking authors to verify things. The code shows clear intentional handling of the top_p parameter. The pattern of removing unsupported parameters is established elsewhere in the code. This appears to be a deliberate design choice rather than a potential issue.
The comment could be pointing out a legitimate concern about error handling - should we warn users when parameters are ignored?
While error handling is important, the comment is phrased as a verification request rather than a concrete suggestion for improvement. If error handling was the concern, it should be stated directly.
Delete the comment as it violates our rule about not asking authors to verify their intentions. The code shows clear intentional handling of the parameter.

</details>

<details>
<summary>6. <code>agents-api/agents_api/routers/responses/create_response.py:128</code></summary>

- **Draft comment:** 
In the reasoning block, if generate_summary is provided, an HTTPException is raised. Confirm that this constraint is clearly documented for API consumers.
- **Reason this comment was not posted:** 
Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%`
The comment is asking the PR author to confirm that a constraint is documented for API consumers. This falls under the category of asking the author to ensure something is documented, which is not allowed according to the rules.

</details>

<details>
<summary>7. <code>agents-api/agents_api/routers/responses/get_response.py:66</code></summary>

- **Draft comment:** 
In the get_response endpoint, the reasoning field is set as: reasoning=Reasoning(effort=None, summary=None). However, the updated Reasoning model no longer has a 'summary' field (it now has 'generate_summary'). This will cause validation errors. Please update to reflect the new model schema.
- **Reason this comment was not posted:** 
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 30% vs. threshold = 50%
Without seeing the actual Reasoning model definition, I can't verify if the comment is correct about the field name change. The comment seems plausible and specific, but we don't have strong evidence. The code has a FIXME comment indicating this is temporary anyway. Making incorrect assumptions about model changes could lead to wrong suggestions.
I might be too cautious - the reviewer may have access to the model definition that we don't see here. The specificity of the comment suggests they've seen the actual model change.
While the comment seems knowledgeable, our rules clearly state we need STRONG EVIDENCE to keep a comment. Without seeing the actual model definition, we can't verify this claim.
Delete the comment since we don't have strong evidence to verify the claimed model schema change, even though the comment seems plausible.

</details>

<details>
<summary>8. <code>agents-api/agents_api/routers/utils/model_converters.py:264</code></summary>

- **Draft comment:** 
The conversion function extends output with function_tool_requests. Confirm that the order and structure of appended function tool calls match client expectations.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>9. <code>agents-api/agents_api/autogen/Responses.py:515</code></summary>

- **Draft comment:** 
The 'effort' field in the Reasoning model is now non-optional and the 'summary' field has been removed in favor of 'generate_summary'. However, other parts of the code (e.g., in get_response) still instantiate Reasoning with effort set to None and include a 'summary' parameter. This inconsistency will cause validation errors. Please update the instantiation to match the new schema or make the field optional if needed.
- **Reason this comment was not posted:** 
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50%
The comment makes a speculative claim about code "elsewhere" that might be broken, but we don't have evidence of that code in the diff. The comment also refers to a 'summary' field change that isn't visible in the diff. We should only comment on what we can see in the diff itself. Making a field required is a valid API change that may be intentional.
The comment could be correct that this is a breaking change that needs attention. Just because we don't see the affected code doesn't mean it doesn't exist.
While true, our rules state we should not make speculative comments or assume problems without clear evidence. The author likely made this change intentionally and tested it.
Delete this comment as it makes speculative claims about code we can't see and assumes the change was unintentional without evidence.

</details>

<details>
<summary>10. <code>agents-api/agents_api/routers/responses/create_response.py:76</code></summary>

- **Draft comment:** 
The code checks for `tool.type == "computer-preview"` to reject unsupported tools. However, the updated tool choice definitions in the models (and Typespec) use a different naming convention (e.g. "computer_use_preview"). This mismatch can lead to unexpected behavior. Please align the literal values for tool types.
- **Reason this comment was not posted:** 
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50%
The comment references models and Typespec definitions that aren't visible in the provided code. Without seeing these definitions, I can't verify if there's actually a naming convention mismatch. The comment makes an assumption about external code we can't see. The change could potentially break functionality if "computer-preview" is actually the correct value.
I might be too cautious - if the reviewer has access to the Typespec definitions, they may be correct about the naming convention. However, making assumptions about unseen code is risky.
Even if the reviewer has seen the Typespec definitions, we need clear evidence in the PR to justify this change. Making changes based on assumptions about external code could introduce bugs.
Delete the comment since we don't have sufficient evidence in the PR to verify the claimed naming convention mismatch.

</details>

<details>
<summary>11. <code>agents-api/agents_api/routers/responses/get_response.py:65</code></summary>

- **Draft comment:** 
When constructing the response, the code uses `Reasoning(effort=None, summary=None)`, but the updated Reasoning model requires a valid literal for 'effort' and no longer supports a 'summary' field. This will likely result in validation errors. Please adjust this instantiation to provide a valid effort value and remove the summary parameter.
- **Reason this comment was not posted:** 
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50%
Without seeing the actual Reasoning model definition from autogen.Responses, I cannot verify if None is invalid for effort or if summary is an invalid field. The FIXME comment suggests this is a temporary solution. The code is actively acknowledging this is incomplete with the comment. Making assumptions about the model requirements without evidence would be speculation.
I don't have access to the Reasoning model definition, so I can't be certain about its requirements. The comment could be correct about the validation issues.
While the comment might be correct, without seeing the model definition we can't verify the claim about validation requirements. The existing FIXME comment already acknowledges this is temporary.
Delete the comment since we lack strong evidence that it's correct about the model requirements, and the code already acknowledges this is a temporary solution with a FIXME.

</details>

<details>
<summary>12. <code>agents-api/agents_api/routers/responses/get_response.py:122</code></summary>

- **Draft comment:** 
There's a FIXME comment regarding the mapping of the tool call's 'call_id' field (i.e. using the same ID as the tool call). Please confirm whether this behavior is intentional and update the code or documentation accordingly.
- **Reason this comment was not posted:** 
Comment did not seem useful. Confidence is useful = `0%` <= threshold `50%`
The comment is asking the PR author to confirm their intention regarding a FIXME comment, which violates the rule against asking for confirmation of intention. It also suggests updating the code or documentation, which is not specific enough to be useful. Therefore, this comment should be removed.

</details>

<details>
<summary>13. <code>agents-api/agents_api/autogen/Responses.py:44</code></summary>

- **Draft comment:** 
In the ComputerTool class, the literal value is set as 'computer-preview', but the accompanying docstring mentions 'computer_use_preview'. Please adjust one of them for consistency.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>14. <code>agents-api/agents_api/autogen/Responses.py:541</code></summary>

- **Draft comment:** 
In the ReasoningContent class, the literal type is set to 'reasoning_summary', yet the docstring incorrectly states that the type is always 'text'. Please update the docstring to correctly reflect the literal value.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>15. <code>agents-api/agents_api/autogen/Responses.py:809</code></summary>

- **Draft comment:** 
In the ToolChoiceTypes class docstring, the phrase 'should to use' should be corrected to 'should use' for clarity.
- **Reason this comment was not posted:** 
Comment did not seem useful. Confidence is useful = `20%` <= threshold `50%`
This comment is suggesting a grammatical correction in a docstring. While it is not asking for confirmation or testing, it is purely informative and does not provide a code suggestion or address a potential issue in the code logic. Therefore, it seems to violate the rule against purely informative comments.

</details>

<details>
<summary>16. <code>agents-api/agents_api/routers/responses/create_response.py:40</code></summary>

- **Draft comment:** 
The docstring for the function process_tool_calls refers to a parameter 'tool_call_responses', but the actual parameter name is 'tool_call_requests'. Consider updating the docstring to match the parameter name for clarity.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>

<details>
<summary>17. <code>typespec/responses/models.tsp:413</code></summary>

- **Draft comment:** 
Inconsistency in ComputerTool model: The doc comment states the type is always `computer_use_preview`, but the literal value is `computer-preview`. They should be consistent.
- **Reason this comment was not posted:** 
Comment was not on a location in the diff, so it can't be submitted as a review comment.

</details>


Workflow ID: <workflowid>`wflow_Djs4jsLdb7g8zh7V`</workflowid>

</details>


----
**Want Ellipsis to fix these issues?** Tag `@ellipsis-dev` in a comment. You can customize Ellipsis with :+1: / :-1: [feedback](https://docs.ellipsis.dev/review), review rules, user-specific overrides, `quiet` mode, and [more](https://docs.ellipsis.dev/config).

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:40:45Z

Review comments:
diff_hunk: @@ -149,11 +178,26 @@ async def create_response(
         while has_tool_calls and iterations < max_iterations:
             iterations += len(tool_call_requests)
 
-            performed_tool_calls.extend(tool_call_requests)
+            # Do not process original function tool calls. They will be sent to the user as is.
+            if (
+                tool_call_requests[0].type == "function"
+                and tool_call_requests[0].function.name != "web_search_preview"
+            ):
+                function_tool_requests.append(
+                    FunctionToolCall(
+                        id="fc_" + tool_call_requests[0].id,
+                        call_id=tool_call_requests[0].id,
+                        name=tool_call_requests[0].function.name,
+                        arguments=tool_call_requests[0].function.arguments,
+                        status="completed",
+                    )
+                )
+                break
 
             # Process tool calls and get updated messages
             current_messages = await process_tool_calls(current_messages, tool_call_requests)
 
+            performed_tool_calls.extend(tool_call_requests)
             # Make a follow-up call to the model with updated messages
path: agents-api/agents_api/routers/responses/create_response.py
user: login: entelligence-ai-pr-reviews[bot]
body: The `performed_tool_calls` list is extended after the `break` statement for function tool calls, causing missing tool calls in the response
<!-- suggestion_start -->

<details>
<summary><strong>📝 Committable Code Suggestion</strong></summary>

> ‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation. 


```suggestion
        while has_tool_calls and iterations < max_iterations:
            iterations += len(tool_call_requests)

            # Do not process original function tool calls. They will be sent to the user as is.
            if (
                tool_call_requests[0].type == "function"
                and tool_call_requests[0].function.name != "web_search_preview"
            ):
                function_tool_requests.append(
                    FunctionToolCall(
                        id="fc_" + tool_call_requests[0].id,
                        call_id=tool_call_requests[0].id,
                        name=tool_call_requests[0].function.name,
                        arguments=tool_call_requests[0].function.arguments,
                        status="completed",
                    )
                )
                performed_tool_calls.extend(tool_call_requests)
                break

            # Process tool calls and get updated messages
            current_messages = await process_tool_calls(current_messages, tool_call_requests)

            performed_tool_calls.extend(tool_call_requests)
            # Make a follow-up call to the model with updated messages
```
</details>
<!-- suggestion_end -->

created_at: 2025-03-19T22:38:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,6 +63,55 @@ async def get_response(
 
     last_entry = last_entries[-1]
 
+    output: list[
+        OutputMessage
+        | FileSearchToolCall
+        | FunctionToolCall
+        | WebSearchToolCall
+        | ComputerToolCall
+        | ReasoningItem
+    ] = []
+
+    for entry in last_entries:
+        if entry.tool_calls:
+            for tool_call in entry.tool_calls:
+                if tool_call.type == "function":
+                    # Specific case of the mapped `web_search_preview` tool call
+                    if tool_call.function.name == "web_search_preview":
+                        output.append(
+                            WebSearchToolCall(
+                                id=tool_call.id,
+                                status="completed",
+                            )
+                        )
+                    else:
+                        # Generic case for all other `function` tool calls
+                        output.append(
+                            FunctionToolCall(
+                                id=tool_call.id,
+                                call_id=tool_call.id,  # FIXME: Check this
+                                name=tool_call.function.name,
+                                arguments=tool_call.function.arguments,
+                                status="completed",
+                            )
+                        )
path: agents-api/agents_api/routers/responses/get_response.py
user: login: entelligence-ai-pr-reviews[bot]
body: The `tool_calls` field is not checked for `None` before accessing it, which could cause runtime errors if `entry.tool_calls` is `None` instead of an empty list
<!-- suggestion_start -->

<details>
<summary><strong>📝 Committable Code Suggestion</strong></summary>

> ‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation. 


```suggestion
        if entry.tool_calls is not None and entry.tool_calls:
            for tool_call in entry.tool_calls:
                if tool_call.type == "function":
                    # Specific case of the mapped `web_search_preview` tool call
                    if tool_call.function.name == "web_search_preview":
                        output.append(
                            WebSearchToolCall(
                                id=tool_call.id,
                                status="completed",
                            )
                        )
                    else:
                        # Generic case for all other `function` tool calls
                        output.append(
                            FunctionToolCall(
                                id=tool_call.id,
                                call_id=tool_call.id,  # FIXME: Check this
                                name=tool_call.function.name,
                                arguments=tool_call.function.arguments,
                                status="completed",
                            )
                        )
```
</details>
<!-- suggestion_end -->

created_at: 2025-03-19T22:38:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -515,10 +515,21 @@ class RankingOptions(BaseModel):
 
 
 class Reasoning(BaseModel):
+    """
+    Reasoning configuration options for o-series models.
+    """
+
     model_config = ConfigDict(
         populate_by_name=True,
     )
-    effort: Literal["low", "medium", "high"] | None = None
+    effort: Literal["low", "medium", "high"]
path: agents-api/agents_api/autogen/Responses.py
user: login: ellipsis-dev[bot]
body: The field `effort` in the `Reasoning` class was made non-optional. Existing clients might rely on it being optional. Consider whether this change is backwards-compatible or if a default should be provided.
```suggestion
    effort: Literal["low", "medium", "high"] | None = None
```
created_at: 2025-03-19T22:40:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,11 +178,26 @@ async def create_response(
         while has_tool_calls and iterations < max_iterations:
             iterations += len(tool_call_requests)
 
-            performed_tool_calls.extend(tool_call_requests)
+            # Do not process original function tool calls. They will be sent to the user as is.
+            if (
+                tool_call_requests[0].type == "function"
path: agents-api/agents_api/routers/responses/create_response.py
user: login: ellipsis-dev[bot]
body: When processing function tool calls, the code branches on `tool_call_requests[0]`. This approach might overlook additional tool calls in the list. Ensure that this single-call assumption is valid.
created_at: 2025-03-19T22:40:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -67,12 +65,111 @@ model CreateResponse {
   metadata?: Record<unknown>;
 }
 
+alias TextResponseFormatConfiguration = ResponseFormatText | TextResponseFormatJsonSchema | ResponseFormatJsonObject;
+
+
+@doc("""
+JSON object response format. An older method of generating JSON responses.
+
+Using `json_schema` is recommended for models that support it. Note that the
+model will not generate JSON without a system or user message instructing it
+to do so.
+""")
+model ResponseFormatJsonObject {
+  @doc("The type of response format being defined. Always `json_object`.")
+  type: "json_object";
+}
+
+
+alias ResponseFormatJsonSchemaSchema = Record<unknown>;
+
+
+@doc("JSON Schema response format. Used to generate structured JSON responses. Learn more about [Structured Outputs](/docs/guides/structured-outputs).")
+model TextResponseFormatJsonSchema {
+  @doc("The type of response format being defined. Always `json_schema`.")
+  type: "json_schema";
+
+  @doc("A description of what the response format is for, used by the model to determine how to respond in the format.")
+  description?: string;
+
+  @doc("The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.")
+  name?: string;
+
+  @doc("The JSON schema that defines the structure of the response.")
+  schema: ResponseFormatJsonSchemaSchema;
+
+  @doc("Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`. To learn more, read the [Structured Outputs guide](/docs/guides/structured-outputs).")
+  strict?: boolean = false;
+}
+
+
+@doc("Default response format. Used to generate text responses.")
+model ResponseFormatText {
+  @doc("The type of response format being defined. Always `text`.")
+  type: "text";
+}
+
+
+@doc("Constraint effort on reasoning models.")
+enum ReasoningEffort {
+  low,
+  medium,
+  high,
+}
+
+@doc("Reasoning configuration options for o-series models.")
+model Reasoning {
+  effort: ReasoningEffort;
+  @doc("A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or `detailed`.")
+  generate_summary?: "concise" | "detailed";
+}
+
+enum ToolChoiceOptions {
+  auto,
+  none,
+  required,
+}
+
+@doc("""
+Indicates that the model should use a built-in tool to generate a response.
+
+[Learn more about built-in tools](/docs/guides/tools).
+""")
+model ToolChoiceTypes {
+  @doc("""
+  The type of hosted tool the model should to use. Learn more about
path: typespec/responses/models.tsp
user: login: ellipsis-dev[bot]
body: Typo in the `ToolChoiceTypes` doc comment: 'The type of hosted tool the model should to use' should be corrected to 'The type of hosted tool the model should use'.
```suggestion
  The type of hosted tool the model should use. Learn more about
```
created_at: 2025-03-19T22:40:45Z
author_association: CONTRIBUTOR

PR comments:
user: login: qodo-free-for-open-source-projects[bot]
created_at: 2025-03-19T22:37:30Z
body: ## PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 3 🔵🔵🔵⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/julep-ai/julep/pull/1277/files#diff-8603f745b55dc5f83fa2a6a2df3e125db811f6f2a514a8f3c17f828b88ca2b87R142-R145'><strong>Error Handling</strong></a>

The reasoning summary feature is rejected with an error, but the error message doesn't provide clear guidance on when this feature will be available or alternative approaches.
</summary>

```python
if create_response_data.reasoning.generate_summary:
    raise HTTPException(
        status_code=400, detail="Generate summary is not supported yet"
    )
```

</details>

<details><summary><a href='https://github.com/julep-ai/julep/pull/1277/files#diff-fd87178521457cc1f7ef723d950b4dcc2dfc4764ab936f89f0bf46017ce17506R268-R277'><strong>Conditional Bug</strong></a>

The code only adds the output message to the output list if output_text exists, but doesn't handle the case where output_text is None or empty properly, potentially causing unexpected behavior.
</summary>

```python
if output_text:
    output.append(
        OutputMessage(
            type="message",
            id=str(chat_response.id),
            status="completed",
            role="assistant",
            content=[OutputText(text=output_text, annotations=[])],
        )
    )
```

</details>

<details><summary><a href='https://github.com/julep-ai/julep/pull/1277/files#diff-3bb57647f0d5a37028a2b5b8d6b4b1249053c0414aa617f431806870f8b8f2ddR128-R130'><strong>Hardcoded Values</strong></a>

The reasoning field is hardcoded with None values in the response, with a FIXME comment indicating this is temporary. This may cause issues if clients expect valid reasoning data.
</summary>

```python
reasoning=Reasoning(
    effort=None, summary=None
),  # FIXME: We are not storing reasoning. So returning None for now.
```

</details>

</td></tr>
</table>

author_association: CONTRIBUTOR
user: login: qodo-free-for-open-source-projects[bot]
created_at: 2025-03-19T22:37:54Z
body: ## PR Code Suggestions ✨

<!-- 9dc1cc0 -->

Explore these optional code suggestions:

<table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=2>Possible issue</td>
<td>



<details><summary>Fix required field validation</summary>

___


**The <code>Reasoning</code> model has been updated to require the <code>effort</code> field, but you're <br>passing <code>None</code> which will cause a validation error. Since the field is required, <br>you should use a valid value or create a different approach for handling missing <br>reasoning data.**

[agents-api/agents_api/routers/responses/get_response.py [128-130]](https://github.com/julep-ai/julep/pull/1277/files#diff-3bb57647f0d5a37028a2b5b8d6b4b1249053c0414aa617f431806870f8b8f2ddR128-R130)

```diff
-reasoning=Reasoning(
-    effort=None, summary=None
-),  # FIXME: We are not storing reasoning. So returning None for now.
+reasoning=None,  # We are not storing reasoning data yet
```

- [ ] **Apply this suggestion** <!-- /improve --apply_suggestion=0 -->

<details><summary>Suggestion importance[1-10]: 9</summary>

__

Why: The suggestion correctly identifies a critical validation error. The updated Reasoning model requires the 'effort' field, but the code is passing None, which would cause runtime errors. Removing the invalid Reasoning object is the right approach.


</details></details></td><td align=center>High

</td></tr><tr><td>



<details><summary>Handle multiple function calls</summary>

___


**The code only processes the first tool call request when checking for function <br>calls, but ignores any additional function calls that might be in the list. This <br>could lead to missing function calls if multiple are returned by the model.**

[agents-api/agents_api/routers/responses/create_response.py [181-195]](https://github.com/julep-ai/julep/pull/1277/files#diff-8603f745b55dc5f83fa2a6a2df3e125db811f6f2a514a8f3c17f828b88ca2b87R181-R195)

```diff
-# Do not process original function tool calls. They will be sent to the user as is.
-if (
-    tool_call_requests[0].type == "function"
-    and tool_call_requests[0].function.name != "web_search_preview"
-):
-    function_tool_requests.append(
-        FunctionToolCall(
-            id="fc_" + tool_call_requests[0].id,
-            call_id=tool_call_requests[0].id,
-            name=tool_call_requests[0].function.name,
-            arguments=tool_call_requests[0].function.arguments,
-            status="completed",
+# Process function tool calls that aren't web search - they'll be sent to the user as is
+function_calls = [tc for tc in tool_call_requests if tc.type == "function" and tc.function.name != "web_search_preview"]
+if function_calls:
+    for fc in function_calls:
+        function_tool_requests.append(
+            FunctionToolCall(
+                id="fc_" + fc.id,
+                call_id=fc.id,
+                name=fc.function.name,
+                arguments=fc.function.arguments,
+                status="completed",
+            )
         )
-    )
     break
```

- [ ] **Apply this suggestion** <!-- /improve --apply_suggestion=1 -->

<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: The suggestion correctly identifies that the current code only processes the first tool call request and ignores additional function calls. The improved code properly handles multiple function calls, which prevents potential data loss and improves functionality.


</details></details></td><td align=center>Medium

</td></tr>
<tr><td align="center" colspan="2">

- [ ] Update <!-- /improve --more_suggestions=true -->

</td><td></td></tr></tbody></table>
author_association: CONTRIBUTOR