Skip to content

Commit 2a42fd4

Browse files
committed
fix: populate required OpenResponses fields with non-null defaults
The OpenResponses conformance spec requires several response fields to always be present as concrete non-null values. llama-stack was returning null for these fields when not explicitly set, causing Zod schema validation to fail on every conformance test. For each affected field, the fix keeps the Python/OpenAPI schema definition unchanged (preserving OpenAI spec conformance scores) while ensuring the actual HTTP response body always carries a concrete value. Fields fixed: - background: always False for non-background responses (was None) - tool_choice: defaults to "auto" when not specified (was None) - truncation: defaults to "disabled" when not specified (was None) - service_tier: defaults to "default" when not specified (was None) - tools: always an array; available_tools() already returns [] not None - temperature: defaults to 1.0 when not specified (was None) - top_p: defaults to 1.0 when not specified (was None) - logprobs: always an empty array when not requested (was None) Changes are applied in _snapshot_response() and _create_refusal_response() in the streaming orchestrator, and in convert_chat_choice_to_response_message() for the non-streaming path. Closes #4987 Closes #4988 Closes #4990 Signed-off-by: Charlie Doern <cdoern@redhat.com>
1 parent 4234fd6 commit 2a42fd4

3 files changed

Lines changed: 16 additions & 9 deletions

File tree

src/llama_stack/providers/inline/agents/meta_reference/responses/streaming.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,21 @@ async def _create_refusal_response(self, violation_message: str) -> OpenAIRespon
215215

216216
# Create a completed refusal response
217217
refusal_response = OpenAIResponseObject(
218+
background=False,
218219
id=self.response_id,
219220
created_at=self.created_at,
220221
model=self.ctx.model,
221222
status="completed",
222223
output=[OpenAIResponseMessage(role="assistant", content=[refusal_content], type="message")],
224+
temperature=self.ctx.temperature if self.ctx.temperature is not None else 1.0,
225+
top_p=self.ctx.top_p if self.ctx.top_p is not None else 1.0,
226+
tools=self.ctx.available_tools(),
227+
tool_choice=self.ctx.tool_choice or OpenAIResponseInputToolChoiceMode.auto,
228+
truncation=self.truncation or "disabled",
223229
max_output_tokens=self.max_output_tokens,
224230
safety_identifier=self.safety_identifier,
225-
service_tier=self.service_tier,
231+
service_tier=self.service_tier or "default",
226232
metadata=self.metadata,
227-
truncation=self.truncation,
228233
store=self.store,
229234
prompt_cache_key=self.prompt_cache_key,
230235
)
@@ -250,6 +255,7 @@ def _snapshot_response(
250255
) -> OpenAIResponseObject:
251256
completed_at = int(time.time()) if status == "completed" else None
252257
return OpenAIResponseObject(
258+
background=False,
253259
created_at=self.created_at,
254260
completed_at=completed_at,
255261
id=self.response_id,
@@ -258,9 +264,10 @@ def _snapshot_response(
258264
status=status,
259265
output=self._clone_outputs(outputs),
260266
text=self.text,
261-
top_p=self.ctx.top_p,
267+
temperature=self.ctx.temperature if self.ctx.temperature is not None else 1.0,
268+
top_p=self.ctx.top_p if self.ctx.top_p is not None else 1.0,
262269
tools=self.ctx.available_tools(),
263-
tool_choice=self.ctx.tool_choice,
270+
tool_choice=self.ctx.tool_choice or OpenAIResponseInputToolChoiceMode.auto,
264271
error=error,
265272
incomplete_details=incomplete_details,
266273
usage=self.accumulated_usage,
@@ -271,9 +278,9 @@ def _snapshot_response(
271278
reasoning=self.reasoning,
272279
max_output_tokens=self.max_output_tokens,
273280
safety_identifier=self.safety_identifier,
274-
service_tier=self.service_tier,
281+
service_tier=self.service_tier or "default",
275282
metadata=self.metadata,
276-
truncation=self.truncation,
283+
truncation=self.truncation or "disabled",
277284
store=self.store,
278285
prompt_cache_key=self.prompt_cache_key,
279286
)
@@ -1050,7 +1057,7 @@ async def _process_streaming_chunks(
10501057
OpenAIResponseOutputMessageContentOutputText(
10511058
text=final_text,
10521059
annotations=[],
1053-
logprobs=chat_response_logprobs if chat_response_logprobs else None,
1060+
logprobs=chat_response_logprobs if chat_response_logprobs else [],
10541061
)
10551062
)
10561063

src/llama_stack/providers/inline/agents/meta_reference/responses/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def convert_chat_choice_to_response_message(
110110
output_content = choice.message.content or ""
111111

112112
annotations, clean_text = _extract_citations_from_text(output_content, citation_files or {})
113-
logprobs = choice.logprobs.content if choice.logprobs and choice.logprobs.content else None
113+
logprobs = choice.logprobs.content if choice.logprobs and choice.logprobs.content else []
114114

115115
return OpenAIResponseMessage(
116116
id=message_id or f"msg_{uuid.uuid4()}",

src/llama_stack_api/openai_responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ class OpenAIResponseIncompleteDetails(BaseModel):
712712
class OpenAIResponseObject(BaseModel):
713713
"""Complete OpenAI response object containing generation results and metadata.
714714
715-
:param background: Whether this response was run in background mode
715+
:param background: Whether this response was run in background mode (default: False)
716716
:param created_at: Unix timestamp when the response was created
717717
:param completed_at: (Optional) Unix timestamp when the response was completed
718718
:param error: (Optional) Error details if the response generation failed

0 commit comments

Comments
 (0)