Skip to content

Conversation

@mldangelo
Copy link
Member

Summary

This PR completes the work started in #3642 by consolidating the renderVarsInObject + maybeLoadFromExternalFile pattern for response_format and functions fields across all providers.

Key changes:

  • Add maybeLoadFromExternalFileWithVars helper for simple config fields (e.g., functions)
  • Add maybeLoadResponseFormatFromExternalFile helper that handles nested schema: file://... references
  • Fix bug in Azure responses where nested schema file references were not being loaded
  • Update OpenAI, Azure, and xAI providers to use the consolidated helpers
  • Add documentation for nested schema file references and variable rendering

Bug fixed: Azure Responses API was not loading nested schema file references. For example:

{
  "type": "json_schema",
  "name": "my_schema",
  "schema": "file://./actual-schema.json"
}

The nested schema file was returned as a string instead of being loaded. OpenAI and xAI already handled this correctly; Azure now matches their behavior.

Test plan

  • Added 14 unit tests for the new helper functions
  • Added test for chained file references (outer file containing nested file reference)
  • Added test for error propagation when nested file doesn't exist
  • All 3541 existing provider tests pass
  • End-to-end tests with examples/openai-responses/promptfooconfig.external-format.yaml (4/4 pass)
  • End-to-end tests with examples/openai-structured-output/promptfooconfig.responses.yaml (4/4 pass)
  • End-to-end tests with examples/openai-structured-output/promptfooconfig.chat.yaml (8/8 pass)

Closes #3227

…ted schema bug

- Add maybeLoadFromExternalFileWithVars for simple config fields with variable rendering
- Add maybeLoadResponseFormatFromExternalFile to handle nested schema file references
- Fix bug in Azure responses where nested schema: file://... was not being loaded
- Update OpenAI, Azure, and xAI providers to use consolidated helper functions
- Add comprehensive unit tests including chained file references and error handling
- Document nested schema file references and variable rendering in provider docs

Closes #3227
@mldangelo mldangelo requested review from a team, MrFlounder, swarnap and typpo as code owners December 30, 2025 01:43
@use-tusk
Copy link
Contributor

use-tusk bot commented Dec 30, 2025

⏩ No test execution environment matched (8b2b78b) View output ↗


View check history

Commit Status Output Created (UTC)
d228fb8 ⏩ No test execution environment matched Output Dec 30, 2025 1:43AM
8b2b78b ⏩ No test execution environment matched Output Jan 2, 2026 8:34PM

View output in GitHub ↗

Copy link
Contributor

@promptfoo-scanner promptfoo-scanner bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 All Clear

I reviewed this PR for LLM security vulnerabilities, focusing on the new response format loading functions and their usage across OpenAI, Azure, and XAI providers. The changes consolidate configuration loading logic and add support for nested schema file references. No LLM security issues were identified.

Minimum severity threshold for this scan: 🟡 Medium | Learn more


Was this helpful?  👍 Yes  |  👎 No 

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

The PR introduces three new utility functions in src/util/file.ts to improve external file loading: maybeLoadFromExternalFileWithVars (renders variables before file loading), maybeLoadResponseFormatFromExternalFile (handles response\_format with nested schema loading), and maybeLoadToolsFromExternalFile (loads tool definitions from files with function execution support). These utilities are exported via src/util/index.ts and integrated across Azure, OpenAI, and XAI providers in both chat and responses modules, replacing previous patterns of manually rendering variables and loading files separately. Documentation for the three providers is updated with examples showing nested file references and variable rendering in file paths using template syntax.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary changes: consolidating response_format loading and fixing an Azure nested schema bug.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about consolidating loading helpers, fixing Azure nested schema handling, and test coverage.
Linked Issues check ✅ Passed The PR fully meets the objectives from issue #3227 by consolidating variable rendering into dedicated helpers (maybeLoadFromExternalFileWithVars, maybeLoadResponseFormatFromExternalFile, maybeLoadToolsFromExternalFile) that hide renderVarsInObject and ensure consistent loading behavior across all providers.
Out of Scope Changes check ✅ Passed All changes are directly related to consolidating response_format and functions loading with variable rendering support. Documentation updates explain the new nested schema reference capability, which is within scope of the consolidation effort.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8759d and d228fb8.

📒 Files selected for processing (13)
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
  • site/docs/providers/xai.md
  • src/providers/azure/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/chat.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/util/file.ts
  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict type checking

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Follow consistent import order enforced by Biome
Use consistent curly braces for all control statements
Prefer const over let; avoid var
Use object shorthand syntax whenever possible
Use async/await for asynchronous code
Use consistent error handling with proper type checks
Avoid re-exporting from files; import directly from the source module
Use the logger with object context (auto-sanitized) for logging: logger.debug('[Component] Message', { headers, body, config })

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Never commit the .env file or expose API keys in code or commit messages

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for database operations

Files:

  • src/util/index.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
site/docs/**/*.md

📄 CodeRabbit inference engine (site/AGENTS.md)

site/docs/**/*.md: Don't modify heading text in documentation (often externally linked)
Avoid embellishment words like 'sophisticated' in documentation
Include front matter with title (under 60 chars), description (150-160 chars), and sidebar_position on all documentation pages
Add title="filename.yaml" attribute only to code blocks containing complete, runnable files
Don't add titles to code block fragments (only complete, runnable files)
Use // highlight-next-line directive to emphasize specific lines in code blocks
Never remove existing highlight directives from code blocks
Use admonition syntax (:::note, :::warning, :::danger) with empty lines around content

Files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
site/**/*.{md,ts,tsx,js,json}

📄 CodeRabbit inference engine (site/AGENTS.md)

site/**/*.{md,ts,tsx,js,json}: Use 'eval' not 'evaluation' in commands and documentation
Use 'Promptfoo' when referring to the company or product, 'promptfoo' when referring to the CLI command or in code

Files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.{ts,tsx}: Never increase test timeouts - fix the slow test instead
Never use .only() or .skip() in committed code
Always clean up mocks in afterEach using vi.resetAllMocks()
All tests require explicit imports from vitest: import { afterEach, describe, expect, it, vi } from 'vitest'
Use Vitest's mocking utilities (vi.mock, vi.fn, vi.spyOn) and prefer shallow mocking over deep mocking
Use vi.clearAllMocks() to clear call history, but use mockReset() for full isolation when mocking with mockReturnValue() or vi.hoisted() in beforeEach
Clean up any test data or mocks after each test
Globals are disabled in vitest configuration. All test utilities must be explicitly imported from vitest

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
{test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}}

📄 CodeRabbit inference engine (AGENTS.md)

{test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}}: Use Vitest for all tests
Test both success and error cases for all functionality

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
test/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Backend tests in test/ should use Vitest with globals enabled (describe, it, expect available without imports)

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
src/providers/**/*.ts

📄 CodeRabbit inference engine (src/providers/AGENTS.md)

src/providers/**/*.ts: Implement ApiProvider interface from src/types/providers.ts for LLM provider integrations
Provider must transform prompts to provider-specific API format and return normalized ProviderResponse
If your provider allocates resources (Python workers, connections, child processes), implement a cleanup() method and register with providerRegistry for automatic cleanup
Use logger with object context (auto-sanitized) for logging as specified in docs/agents/logging.md
OpenAI-compatible providers should extend OpenAiChatCompletionProvider base class
Config priority order: Explicit options > Environment variables > Provider defaults

Files:

  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
src/providers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Document provider configurations following examples in existing code

Files:

  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
🧠 Learnings (34)
📚 Learning: 2025-12-09T06:08:55.096Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:55.096Z
Learning: Applies to src/commands/**/*.ts : Use `setupEnv()` for environment setup in CLI commands

Applied to files:

  • src/util/index.ts
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When demonstrating specialized capabilities (vision, audio, etc.), use models that support those features in configuration files

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Always include the YAML schema reference at the top of configuration files: '# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json'

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Each example directory must include a `promptfooconfig.yaml` with schema reference `https://promptfoo.dev/config-schema.json`

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use current model identifiers for OpenAI, Anthropic, and Google providers from the documentation in `site/docs/providers/`

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Include a working promptfooconfig.yaml (or equivalent) file in each example

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Include a mix of providers when comparing model performance in configuration files

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use `file://` prefix for external file references in promptfooconfig.yaml

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : For OpenAI, prefer models like 'openai:o3-mini' and 'openai:gpt-4o-mini' in configuration files

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use YAML field order in promptfooconfig.yaml: description, env, prompts, providers, defaultTest, scenarios, tests

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Follow the specific field order in all configuration files: description, env (optional), prompts, providers, defaultTest (optional), scenarios (optional), tests

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Vitest's mocking utilities (`vi.mock`, `vi.fn`, `vi.spyOn`) and prefer shallow mocking over deep mocking

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-28T00:26:53.706Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/app/AGENTS.md:0-0
Timestamp: 2025-12-28T00:26:53.706Z
Learning: Applies to src/app/src/**/*.test.{ts,tsx} : Use Vitest with `vi.fn()` for mocks and `vi.mock()` for modules. See 'src/app/src/hooks/usePageMeta.test.ts' for patterns

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/test/providers/**/*.ts : Provider tests must NEVER make real API calls - mock all HTTP requests using `vi.mock`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use `vi.clearAllMocks()` to clear call history, but use `mockReset()` for full isolation when mocking with `mockReturnValue()` or `vi.hoisted()` in `beforeEach`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : All tests require explicit imports from vitest: `import { afterEach, describe, expect, it, vi } from 'vitest'`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Always clean up mocks in `afterEach` using `vi.resetAllMocks()`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Clean up any test data or mocks after each test

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/providers/*.test.{ts,tsx} : For provider tests, cover: success case, error cases (4xx, 5xx, rate limits), configuration validation, and token usage tracking

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Globals are disabled in vitest configuration. All test utilities must be explicitly imported from `vitest`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Backend tests in `test/` should use Vitest with globals enabled (`describe`, `it`, `expect` available without imports)

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to src/app/**/*.{test,spec}.{ts,tsx} : Frontend tests in `src/app/` should use Vitest with explicit imports

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to {test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}} : Use Vitest for all tests

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/test/providers/**/*.ts : Test provider success AND error cases, including rate limits, timeouts, and invalid configs

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Mock external dependencies but not the code being tested

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/site/docs/providers/**/*.md : Provider documentation should be added in `site/docs/providers/`

Applied to files:

  • site/docs/providers/azure.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When referencing external files in configuration, always use the 'file://' prefix

Applied to files:

  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/**/*.ts : OpenAI-compatible providers should extend `OpenAiChatCompletionProvider` base class

Applied to files:

  • src/providers/openai/chat.ts
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When creating examples for specific providers, always use the latest available model versions for that provider in configuration files

Applied to files:

  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/README.md : When creating examples for specific providers, compare to similar providers where appropriate in the README

Applied to files:

  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:09:21.914Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/server/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:21.914Z
Learning: Applies to src/server/**/*.{ts,tsx} : Wrap responses in { success, data/error } format

Applied to files:

  • src/providers/xai/responses.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/**/*.ts : Provider must transform prompts to provider-specific API format and return normalized `ProviderResponse`

Applied to files:

  • src/providers/xai/responses.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to {test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}} : Test both success and error cases for all functionality

Applied to files:

  • test/util/file.test.ts
📚 Learning: 2025-12-20T02:05:23.936Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/redteam/AGENTS.md:0-0
Timestamp: 2025-12-20T02:05:23.936Z
Learning: Applies to src/redteam/test/redteam/**/*.{ts,js} : Add tests for new plugins in the test/redteam/ directory following the reference pattern in src/redteam/plugins/pii.ts

Applied to files:

  • test/util/file.test.ts
🧬 Code graph analysis (8)
src/providers/openai/chat.ts (2)
src/util/file.ts (2)
  • maybeLoadFromExternalFileWithVars (387-393)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (2)
  • maybeLoadFromExternalFileWithVars (9-9)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/azure/responses.ts (2)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (1)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/openai/responses.ts (1)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/providers/openai/util.ts (2)
src/util/file.ts (1)
  • maybeLoadFromExternalFileWithVars (387-393)
src/util/index.ts (1)
  • maybeLoadFromExternalFileWithVars (9-9)
src/providers/xai/responses.ts (2)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (1)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/azure/chat.ts (2)
src/util/file.ts (2)
  • maybeLoadResponseFormatFromExternalFile (408-445)
  • maybeLoadFromExternalFileWithVars (387-393)
src/util/index.ts (2)
  • maybeLoadResponseFormatFromExternalFile (10-10)
  • maybeLoadFromExternalFileWithVars (9-9)
src/util/file.ts (1)
src/util/render.ts (1)
  • renderVarsInObject (100-123)
test/util/file.test.ts (2)
src/util/file.ts (2)
  • maybeLoadFromExternalFileWithVars (387-393)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (2)
  • maybeLoadFromExternalFileWithVars (9-9)
  • maybeLoadResponseFormatFromExternalFile (10-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: Test on Node 22.x and windows-latest (shard 1/3)
  • GitHub Check: Test on Node 24.x and ubuntu-latest
  • GitHub Check: Test on Node 24.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 20.x and macOS-latest
  • GitHub Check: Test on Node 22.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 20.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 24.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 22.x and ubuntu-latest
  • GitHub Check: Test on Node 20.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 22.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 24.x and windows-latest (shard 1/3)
  • GitHub Check: webui tests
  • GitHub Check: Test on Node 20.x and windows-latest (shard 1/3)
  • GitHub Check: Test on Node 22.x and macOS-latest
  • GitHub Check: Build on Node 22.x
  • GitHub Check: Build on Node 24.x
  • GitHub Check: Test on Node 20.x and ubuntu-latest
  • GitHub Check: Redteam (Staging API)
  • GitHub Check: Redteam (Production API)
  • GitHub Check: Build on Node 20.x
  • GitHub Check: Build Docs
  • GitHub Check: Style Check
  • GitHub Check: Share Test
  • GitHub Check: security-scan
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (17)
src/util/file.ts (1)

371-445: New helpers cleanly centralize var rendering and nested response_format loading

maybeLoadFromExternalFileWithVars and maybeLoadResponseFormatFromExternalFile correctly compose renderVarsInObject with maybeLoadFromExternalFile, and the latter safely:

  • Early-returns for undefined/null.
  • Handles both top-level schema and json_schema.schema.
  • Loads nested file://... schemas without mutating the original object.

This matches the documented behavior and should fix the nested-schema bug without regressions.

site/docs/providers/xai.md (1)

435-443: xAI structured-output docs align with new external schema behavior

The added example and note about nested file references + variable rendering accurately reflect the new response_format loading semantics and are consistent with the OpenAI docs reference.

site/docs/providers/openai.md (1)

1039-1055: Nested schema and templated path examples correctly describe implementation

The new response_format.json example with "schema": "file://./schemas/event-schema.json" and the templated response_format: file://./schemas/{{ schema_name }}.json snippet map directly onto the logic in maybeLoadResponseFormatFromExternalFile, so users will see behavior consistent with the docs.

src/providers/openai/util.ts (1)

2-2: validateFunctionCall helper swap preserves behavior and centralizes loading

Switching to maybeLoadFromExternalFileWithVars(functions, vars) keeps the previous behavior (var rendering + optional external file load) while reusing the new shared utility. The optional chaining on interpolatedFunctions?.find(...) still guards functions being undefined, so the change is safe.

Also applies to: 640-643

site/docs/providers/azure.md (1)

183-199: Azure Responses API docs correctly describe nested schema and templated paths

The new nested schema: "file://./schemas/output-schema.json" example and the templated response_format path align with maybeLoadResponseFormatFromExternalFile, so Azure docs now reflect the same external-schema behavior as OpenAI/XAI.

src/util/index.ts (1)

8-15: Exposing new file helpers via the util barrel is consistent and useful

Re-exporting maybeLoadFromExternalFileWithVars and maybeLoadResponseFormatFromExternalFile alongside the existing file utilities keeps provider imports consistent (../../util/index) and matches this file’s backwards‑compatibility purpose.

src/providers/openai/chat.ts (1)

15-20: Chat provider now correctly uses shared helpers for functions and response_format

Wiring functions through maybeLoadFromExternalFileWithVars and response_format through maybeLoadResponseFormatFromExternalFile keeps semantics identical to the prior inline render+load logic, while adding nested schema support and centralizing behavior with other providers. As both helpers are synchronous, this fits cleanly into getOpenAiBody.

Also applies to: 259-273

src/providers/openai/responses.ts (1)

4-8: Responses provider correctly delegates response_format loading to nested-schema helper

Using maybeLoadResponseFormatFromExternalFile(config.response_format, context?.vars) to derive responseFormat, then reading schema from responseFormat.schema || responseFormat.json_schema?.schema, ensures:

  • Top-level and nested file:// schema references are resolved once.
  • The resulting text.format is always built from the loaded schema.
    Storing response_format: responseFormat back into the returned config keeps downstream processing consistent.

Also applies to: 174-193

src/providers/xai/responses.ts (1)

4-7: LGTM! Clean integration of consolidated response_format loading.

The changes successfully integrate the new maybeLoadResponseFormatFromExternalFile helper, which handles both variable rendering and nested schema loading in a single call. The updated comment on line 244 correctly notes that schema loading is handled by the helper, eliminating the previous double-loading issue.

Also applies to: 232-236, 244-245

test/util/file.test.ts (2)

1262-1306: Excellent test coverage for maybeLoadFromExternalFileWithVars.

The test suite comprehensively covers all expected scenarios: variable rendering in file paths and objects, pass-through behavior, and edge cases with undefined/null values.


1308-1520: Comprehensive test coverage for maybeLoadResponseFormatFromExternalFile.

The test suite thoroughly validates all critical scenarios including:

  • Basic file loading and variable rendering
  • Nested schema loading (both schema and json_schema.schema paths)
  • Chained file references (lines 1460-1504) - this is the key test for the Azure bug fix where an outer file contains a nested file reference
  • Error propagation when nested files are missing

The chained file reference test is particularly valuable as it validates the fix for the Azure double-loading bug mentioned in the PR objectives.

src/providers/azure/chat.ts (1)

10-15: LGTM! Consistent adoption of consolidated loading helpers.

The code correctly uses:

  • maybeLoadResponseFormatFromExternalFile for response_format (with nested schema support)
  • maybeLoadFromExternalFileWithVars for the simpler functions field

Both helpers properly pass context?.vars for variable rendering, and the comment on line 135 accurately describes the nested schema loading capability.

Also applies to: 135-143, 182-182

test/providers/azure/responses.test.ts (3)

6-9: LGTM! Test setup correctly updated for new helpers.

The test properly imports and mocks maybeLoadResponseFormatFromExternalFile. The change from vi.clearAllMocks() to vi.resetAllMocks() on line 30 is correct per coding guidelines, which specify using mockReset() or vi.resetAllMocks() for full isolation when using mockReturnValue() in test setup.

Also applies to: 18-24, 30-30


130-130: Excellent validation of the double-loading bug fix.

Line 144 explicitly verifies that maybeLoadResponseFormatFromExternalFile is called only once, which validates the fix for the Azure nested schema double-loading bug mentioned in the PR objectives. This is a critical test case.

Also applies to: 140-144


155-157: Test scenarios correctly updated for new loading path.

The mock implementations properly simulate:

  • Line 155-157: Pass-through behavior for inline schemas
  • Line 339-341: Error propagation for missing files

Both scenarios align with the expected behavior of maybeLoadResponseFormatFromExternalFile.

Also applies to: 339-341

src/providers/azure/responses.ts (2)

4-8: LGTM! Proper implementation of consolidated response_format loading.

The code correctly uses maybeLoadResponseFormatFromExternalFile with context?.vars for variable rendering. The comment on line 142 accurately notes that the schema is already loaded by the helper, and line 151 correctly uses the loaded schema without attempting to reload it. This fix addresses the Azure nested schema double-loading bug mentioned in the PR objectives.

Also applies to: 127-131, 142-151


231-238: Improved error handling with detailed error messages.

The validation logic now uses maybeLoadResponseFormatFromExternalFile consistently, and the error message on lines 234-238 provides helpful context including the specific loading error and guidance for resolution.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/providers/openai/util.ts (1)

382-478: Remove duplicate gpt-realtime entry or clarify versioning with distinct IDs

OPENAI_REALTIME_MODELS has two entries with id: 'gpt-realtime'. The .find() method in the cost calculation returns only the first match, so the second entry is never used. More critically, the first entry's prices ($32/$64 per 1M tokens) significantly exceed OpenAI's current pricing of $0.50/$16.00 per 1M tokens for gpt-realtime, causing incorrect cost calculations. If the second entry represents an older pricing snapshot, use a distinct versioned ID (e.g., 'gpt-realtime-2024-10-01'); otherwise, remove it and update the first entry with correct current pricing.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8759d and d228fb8.

📒 Files selected for processing (13)
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
  • site/docs/providers/xai.md
  • src/providers/azure/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/chat.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/util/file.ts
  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with strict type checking

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Follow consistent import order enforced by Biome
Use consistent curly braces for all control statements
Prefer const over let; avoid var
Use object shorthand syntax whenever possible
Use async/await for asynchronous code
Use consistent error handling with proper type checks
Avoid re-exporting from files; import directly from the source module
Use the logger with object context (auto-sanitized) for logging: logger.debug('[Component] Message', { headers, body, config })

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
**/*.{ts,tsx,js,jsx,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Never commit the .env file or expose API keys in code or commit messages

Files:

  • src/util/index.ts
  • test/providers/azure/responses.test.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
  • test/util/file.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for database operations

Files:

  • src/util/index.ts
  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
  • src/util/file.ts
site/docs/**/*.md

📄 CodeRabbit inference engine (site/AGENTS.md)

site/docs/**/*.md: Don't modify heading text in documentation (often externally linked)
Avoid embellishment words like 'sophisticated' in documentation
Include front matter with title (under 60 chars), description (150-160 chars), and sidebar_position on all documentation pages
Add title="filename.yaml" attribute only to code blocks containing complete, runnable files
Don't add titles to code block fragments (only complete, runnable files)
Use // highlight-next-line directive to emphasize specific lines in code blocks
Never remove existing highlight directives from code blocks
Use admonition syntax (:::note, :::warning, :::danger) with empty lines around content

Files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
site/**/*.{md,ts,tsx,js,json}

📄 CodeRabbit inference engine (site/AGENTS.md)

site/**/*.{md,ts,tsx,js,json}: Use 'eval' not 'evaluation' in commands and documentation
Use 'Promptfoo' when referring to the company or product, 'promptfoo' when referring to the CLI command or in code

Files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.{ts,tsx}: Never increase test timeouts - fix the slow test instead
Never use .only() or .skip() in committed code
Always clean up mocks in afterEach using vi.resetAllMocks()
All tests require explicit imports from vitest: import { afterEach, describe, expect, it, vi } from 'vitest'
Use Vitest's mocking utilities (vi.mock, vi.fn, vi.spyOn) and prefer shallow mocking over deep mocking
Use vi.clearAllMocks() to clear call history, but use mockReset() for full isolation when mocking with mockReturnValue() or vi.hoisted() in beforeEach
Clean up any test data or mocks after each test
Globals are disabled in vitest configuration. All test utilities must be explicitly imported from vitest

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
{test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}}

📄 CodeRabbit inference engine (AGENTS.md)

{test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}}: Use Vitest for all tests
Test both success and error cases for all functionality

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
test/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Backend tests in test/ should use Vitest with globals enabled (describe, it, expect available without imports)

Files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
src/providers/**/*.ts

📄 CodeRabbit inference engine (src/providers/AGENTS.md)

src/providers/**/*.ts: Implement ApiProvider interface from src/types/providers.ts for LLM provider integrations
Provider must transform prompts to provider-specific API format and return normalized ProviderResponse
If your provider allocates resources (Python workers, connections, child processes), implement a cleanup() method and register with providerRegistry for automatic cleanup
Use logger with object context (auto-sanitized) for logging as specified in docs/agents/logging.md
OpenAI-compatible providers should extend OpenAiChatCompletionProvider base class
Config priority order: Explicit options > Environment variables > Provider defaults

Files:

  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
src/providers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Document provider configurations following examples in existing code

Files:

  • src/providers/openai/chat.ts
  • src/providers/azure/responses.ts
  • src/providers/openai/responses.ts
  • src/providers/openai/util.ts
  • src/providers/xai/responses.ts
  • src/providers/azure/chat.ts
🧠 Learnings (34)
📚 Learning: 2025-12-09T06:08:55.096Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:55.096Z
Learning: Applies to src/commands/**/*.ts : Use `setupEnv()` for environment setup in CLI commands

Applied to files:

  • src/util/index.ts
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When demonstrating specialized capabilities (vision, audio, etc.), use models that support those features in configuration files

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Always include the YAML schema reference at the top of configuration files: '# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json'

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Each example directory must include a `promptfooconfig.yaml` with schema reference `https://promptfoo.dev/config-schema.json`

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use current model identifiers for OpenAI, Anthropic, and Google providers from the documentation in `site/docs/providers/`

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Include a working promptfooconfig.yaml (or equivalent) file in each example

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Include a mix of providers when comparing model performance in configuration files

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use `file://` prefix for external file references in promptfooconfig.yaml

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : For OpenAI, prefer models like 'openai:o3-mini' and 'openai:gpt-4o-mini' in configuration files

Applied to files:

  • site/docs/providers/xai.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:08:22.578Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: examples/AGENTS.md:0-0
Timestamp: 2025-12-09T06:08:22.578Z
Learning: Applies to examples/**/promptfooconfig.yaml : Use YAML field order in promptfooconfig.yaml: description, env, prompts, providers, defaultTest, scenarios, tests

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : Follow the specific field order in all configuration files: description, env (optional), prompts, providers, defaultTest (optional), scenarios (optional), tests

Applied to files:

  • site/docs/providers/xai.md
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Vitest's mocking utilities (`vi.mock`, `vi.fn`, `vi.spyOn`) and prefer shallow mocking over deep mocking

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-28T00:26:53.706Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/app/AGENTS.md:0-0
Timestamp: 2025-12-28T00:26:53.706Z
Learning: Applies to src/app/src/**/*.test.{ts,tsx} : Use Vitest with `vi.fn()` for mocks and `vi.mock()` for modules. See 'src/app/src/hooks/usePageMeta.test.ts' for patterns

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/test/providers/**/*.ts : Provider tests must NEVER make real API calls - mock all HTTP requests using `vi.mock`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use `vi.clearAllMocks()` to clear call history, but use `mockReset()` for full isolation when mocking with `mockReturnValue()` or `vi.hoisted()` in `beforeEach`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : All tests require explicit imports from vitest: `import { afterEach, describe, expect, it, vi } from 'vitest'`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Always clean up mocks in `afterEach` using `vi.resetAllMocks()`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Clean up any test data or mocks after each test

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/providers/*.test.{ts,tsx} : For provider tests, cover: success case, error cases (4xx, 5xx, rate limits), configuration validation, and token usage tracking

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Applies to test/**/*.test.{ts,tsx} : Globals are disabled in vitest configuration. All test utilities must be explicitly imported from `vitest`

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Backend tests in `test/` should use Vitest with globals enabled (`describe`, `it`, `expect` available without imports)

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to src/app/**/*.{test,spec}.{ts,tsx} : Frontend tests in `src/app/` should use Vitest with explicit imports

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to {test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}} : Use Vitest for all tests

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/test/providers/**/*.ts : Test provider success AND error cases, including rate limits, timeouts, and invalid configs

Applied to files:

  • test/providers/azure/responses.test.ts
  • test/util/file.test.ts
📚 Learning: 2025-12-11T07:30:27.904Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2025-12-11T07:30:27.904Z
Learning: Mock external dependencies but not the code being tested

Applied to files:

  • test/providers/azure/responses.test.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/site/docs/providers/**/*.md : Provider documentation should be added in `site/docs/providers/`

Applied to files:

  • site/docs/providers/azure.md
📚 Learning: 2025-07-18T17:25:38.444Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.444Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When referencing external files in configuration, always use the 'file://' prefix

Applied to files:

  • site/docs/providers/azure.md
  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/**/*.ts : OpenAI-compatible providers should extend `OpenAiChatCompletionProvider` base class

Applied to files:

  • src/providers/openai/chat.ts
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/promptfooconfig*.yaml : When creating examples for specific providers, always use the latest available model versions for that provider in configuration files

Applied to files:

  • site/docs/providers/openai.md
📚 Learning: 2025-07-18T17:25:38.445Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: .cursor/rules/examples.mdc:0-0
Timestamp: 2025-07-18T17:25:38.445Z
Learning: Applies to examples/*/README.md : When creating examples for specific providers, compare to similar providers where appropriate in the README

Applied to files:

  • site/docs/providers/openai.md
📚 Learning: 2025-12-09T06:09:21.914Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/server/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:21.914Z
Learning: Applies to src/server/**/*.{ts,tsx} : Wrap responses in { success, data/error } format

Applied to files:

  • src/providers/xai/responses.ts
📚 Learning: 2025-12-09T06:09:06.028Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/providers/AGENTS.md:0-0
Timestamp: 2025-12-09T06:09:06.028Z
Learning: Applies to src/providers/**/*.ts : Provider must transform prompts to provider-specific API format and return normalized `ProviderResponse`

Applied to files:

  • src/providers/xai/responses.ts
📚 Learning: 2025-12-29T16:16:50.330Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T16:16:50.330Z
Learning: Applies to {test/**/*.{ts,tsx},src/app/**/*.{test,spec}.{ts,tsx}} : Test both success and error cases for all functionality

Applied to files:

  • test/util/file.test.ts
📚 Learning: 2025-12-20T02:05:23.936Z
Learnt from: CR
Repo: promptfoo/promptfoo PR: 0
File: src/redteam/AGENTS.md:0-0
Timestamp: 2025-12-20T02:05:23.936Z
Learning: Applies to src/redteam/test/redteam/**/*.{ts,js} : Add tests for new plugins in the test/redteam/ directory following the reference pattern in src/redteam/plugins/pii.ts

Applied to files:

  • test/util/file.test.ts
🧬 Code graph analysis (8)
src/providers/openai/chat.ts (2)
src/util/file.ts (2)
  • maybeLoadFromExternalFileWithVars (387-393)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (2)
  • maybeLoadFromExternalFileWithVars (9-9)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/azure/responses.ts (2)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (1)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/openai/responses.ts (1)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/providers/openai/util.ts (2)
src/util/file.ts (1)
  • maybeLoadFromExternalFileWithVars (387-393)
src/util/index.ts (1)
  • maybeLoadFromExternalFileWithVars (9-9)
src/providers/xai/responses.ts (2)
src/util/file.ts (1)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (1)
  • maybeLoadResponseFormatFromExternalFile (10-10)
src/providers/azure/chat.ts (2)
src/util/file.ts (2)
  • maybeLoadResponseFormatFromExternalFile (408-445)
  • maybeLoadFromExternalFileWithVars (387-393)
src/util/index.ts (2)
  • maybeLoadResponseFormatFromExternalFile (10-10)
  • maybeLoadFromExternalFileWithVars (9-9)
src/util/file.ts (1)
src/util/render.ts (1)
  • renderVarsInObject (100-123)
test/util/file.test.ts (2)
src/util/file.ts (2)
  • maybeLoadFromExternalFileWithVars (387-393)
  • maybeLoadResponseFormatFromExternalFile (408-445)
src/util/index.ts (2)
  • maybeLoadFromExternalFileWithVars (9-9)
  • maybeLoadResponseFormatFromExternalFile (10-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: Test on Node 22.x and windows-latest (shard 1/3)
  • GitHub Check: Test on Node 24.x and ubuntu-latest
  • GitHub Check: Test on Node 24.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 20.x and macOS-latest
  • GitHub Check: Test on Node 22.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 20.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 24.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 22.x and ubuntu-latest
  • GitHub Check: Test on Node 20.x and windows-latest (shard 3/3)
  • GitHub Check: Test on Node 22.x and windows-latest (shard 2/3)
  • GitHub Check: Test on Node 24.x and windows-latest (shard 1/3)
  • GitHub Check: webui tests
  • GitHub Check: Test on Node 20.x and windows-latest (shard 1/3)
  • GitHub Check: Test on Node 22.x and macOS-latest
  • GitHub Check: Build on Node 22.x
  • GitHub Check: Build on Node 24.x
  • GitHub Check: Test on Node 20.x and ubuntu-latest
  • GitHub Check: Redteam (Staging API)
  • GitHub Check: Redteam (Production API)
  • GitHub Check: Build on Node 20.x
  • GitHub Check: Build Docs
  • GitHub Check: Style Check
  • GitHub Check: Share Test
  • GitHub Check: security-scan
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (17)
src/util/file.ts (1)

371-445: New helpers cleanly centralize var rendering and nested response_format loading

maybeLoadFromExternalFileWithVars and maybeLoadResponseFormatFromExternalFile correctly compose renderVarsInObject with maybeLoadFromExternalFile, and the latter safely:

  • Early-returns for undefined/null.
  • Handles both top-level schema and json_schema.schema.
  • Loads nested file://... schemas without mutating the original object.

This matches the documented behavior and should fix the nested-schema bug without regressions.

site/docs/providers/xai.md (1)

435-443: xAI structured-output docs align with new external schema behavior

The added example and note about nested file references + variable rendering accurately reflect the new response_format loading semantics and are consistent with the OpenAI docs reference.

site/docs/providers/openai.md (1)

1039-1055: Nested schema and templated path examples correctly describe implementation

The new response_format.json example with "schema": "file://./schemas/event-schema.json" and the templated response_format: file://./schemas/{{ schema_name }}.json snippet map directly onto the logic in maybeLoadResponseFormatFromExternalFile, so users will see behavior consistent with the docs.

src/providers/openai/util.ts (1)

2-2: validateFunctionCall helper swap preserves behavior and centralizes loading

Switching to maybeLoadFromExternalFileWithVars(functions, vars) keeps the previous behavior (var rendering + optional external file load) while reusing the new shared utility. The optional chaining on interpolatedFunctions?.find(...) still guards functions being undefined, so the change is safe.

Also applies to: 640-643

site/docs/providers/azure.md (1)

183-199: Azure Responses API docs correctly describe nested schema and templated paths

The new nested schema: "file://./schemas/output-schema.json" example and the templated response_format path align with maybeLoadResponseFormatFromExternalFile, so Azure docs now reflect the same external-schema behavior as OpenAI/XAI.

src/util/index.ts (1)

8-15: Exposing new file helpers via the util barrel is consistent and useful

Re-exporting maybeLoadFromExternalFileWithVars and maybeLoadResponseFormatFromExternalFile alongside the existing file utilities keeps provider imports consistent (../../util/index) and matches this file’s backwards‑compatibility purpose.

src/providers/openai/chat.ts (1)

15-20: Chat provider now correctly uses shared helpers for functions and response_format

Wiring functions through maybeLoadFromExternalFileWithVars and response_format through maybeLoadResponseFormatFromExternalFile keeps semantics identical to the prior inline render+load logic, while adding nested schema support and centralizing behavior with other providers. As both helpers are synchronous, this fits cleanly into getOpenAiBody.

Also applies to: 259-273

src/providers/openai/responses.ts (1)

4-8: Responses provider correctly delegates response_format loading to nested-schema helper

Using maybeLoadResponseFormatFromExternalFile(config.response_format, context?.vars) to derive responseFormat, then reading schema from responseFormat.schema || responseFormat.json_schema?.schema, ensures:

  • Top-level and nested file:// schema references are resolved once.
  • The resulting text.format is always built from the loaded schema.
    Storing response_format: responseFormat back into the returned config keeps downstream processing consistent.

Also applies to: 174-193

src/providers/xai/responses.ts (1)

4-7: LGTM! Clean integration of consolidated response_format loading.

The changes successfully integrate the new maybeLoadResponseFormatFromExternalFile helper, which handles both variable rendering and nested schema loading in a single call. The updated comment on line 244 correctly notes that schema loading is handled by the helper, eliminating the previous double-loading issue.

Also applies to: 232-236, 244-245

test/util/file.test.ts (2)

1262-1306: Excellent test coverage for maybeLoadFromExternalFileWithVars.

The test suite comprehensively covers all expected scenarios: variable rendering in file paths and objects, pass-through behavior, and edge cases with undefined/null values.


1308-1520: Comprehensive test coverage for maybeLoadResponseFormatFromExternalFile.

The test suite thoroughly validates all critical scenarios including:

  • Basic file loading and variable rendering
  • Nested schema loading (both schema and json_schema.schema paths)
  • Chained file references (lines 1460-1504) - this is the key test for the Azure bug fix where an outer file contains a nested file reference
  • Error propagation when nested files are missing

The chained file reference test is particularly valuable as it validates the fix for the Azure double-loading bug mentioned in the PR objectives.

src/providers/azure/chat.ts (1)

10-15: LGTM! Consistent adoption of consolidated loading helpers.

The code correctly uses:

  • maybeLoadResponseFormatFromExternalFile for response_format (with nested schema support)
  • maybeLoadFromExternalFileWithVars for the simpler functions field

Both helpers properly pass context?.vars for variable rendering, and the comment on line 135 accurately describes the nested schema loading capability.

Also applies to: 135-143, 182-182

test/providers/azure/responses.test.ts (3)

6-9: LGTM! Test setup correctly updated for new helpers.

The test properly imports and mocks maybeLoadResponseFormatFromExternalFile. The change from vi.clearAllMocks() to vi.resetAllMocks() on line 30 is correct per coding guidelines, which specify using mockReset() or vi.resetAllMocks() for full isolation when using mockReturnValue() in test setup.

Also applies to: 18-24, 30-30


130-130: Excellent validation of the double-loading bug fix.

Line 144 explicitly verifies that maybeLoadResponseFormatFromExternalFile is called only once, which validates the fix for the Azure nested schema double-loading bug mentioned in the PR objectives. This is a critical test case.

Also applies to: 140-144


155-157: Test scenarios correctly updated for new loading path.

The mock implementations properly simulate:

  • Line 155-157: Pass-through behavior for inline schemas
  • Line 339-341: Error propagation for missing files

Both scenarios align with the expected behavior of maybeLoadResponseFormatFromExternalFile.

Also applies to: 339-341

src/providers/azure/responses.ts (2)

4-8: LGTM! Proper implementation of consolidated response_format loading.

The code correctly uses maybeLoadResponseFormatFromExternalFile with context?.vars for variable rendering. The comment on line 142 accurately notes that the schema is already loaded by the helper, and line 151 correctly uses the loaded schema without attempting to reload it. This fix addresses the Azure nested schema double-loading bug mentioned in the PR objectives.

Also applies to: 127-131, 142-151


231-238: Improved error handling with detailed error messages.

The validation logic now uses maybeLoadResponseFormatFromExternalFile consistently, and the error message on lines 234-238 provides helpful context including the specific loading error and guidance for resolution.

@diffray-bot
Copy link

Changes Summary

This PR consolidates the renderVarsInObject + maybeLoadFromExternalFile pattern across OpenAI, Azure, and xAI providers by introducing two new helper functions. It also fixes a bug in Azure Responses API where nested schema file references were not being loaded, bringing Azure's behavior in line with OpenAI and xAI.

Type: bugfix

Components Affected: Azure Chat Provider, Azure Responses Provider, OpenAI Chat Provider, OpenAI Responses Provider, OpenAI Util, xAI Responses Provider, File utilities, Provider documentation

Files Changed
File Summary Change Impact
/tmp/workspace/src/util/file.ts Added two new helper functions: maybeLoadFromExternalFileWithVars for simple config fields and maybeLoadResponseFormatFromExternalFile for response_format with nested schema loading support ✏️ 🔴
/tmp/workspace/src/util/index.ts Exported the two new helper functions from file utilities ✏️ 🟢
/tmp/workspace/src/providers/azure/chat.ts Updated to use new maybeLoadResponseFormatFromExternalFile and maybeLoadFromExternalFileWithVars helpers, eliminating duplicate renderVarsInObject + maybeLoadFromExternalFile patterns ✏️ 🟡
/tmp/workspace/src/providers/azure/responses.ts Fixed bug where nested schema file references were not loaded, now uses maybeLoadResponseFormatFromExternalFile for proper nested schema handling ✏️ 🔴
/tmp/workspace/src/providers/openai/chat.ts Refactored to use new maybeLoadResponseFormatFromExternalFile and maybeLoadFromExternalFileWithVars helpers instead of manual renderVarsInObject + maybeLoadFromExternalFile pattern ✏️ 🟡
/tmp/workspace/src/providers/openai/responses.ts Refactored to use maybeLoadResponseFormatFromExternalFile, eliminating double-loading issue where nested schema was being loaded twice ✏️ 🟡
/tmp/workspace/src/providers/openai/util.ts Updated validateFunctionCall to use maybeLoadFromExternalFileWithVars helper instead of manual pattern ✏️ 🟢
/tmp/workspace/src/providers/xai/responses.ts Refactored to use maybeLoadResponseFormatFromExternalFile, eliminating double-loading of nested schema ✏️ 🟡
...orkspace/test/providers/azure/responses.test.ts Updated mocks to use maybeLoadResponseFormatFromExternalFile instead of maybeLoadFromExternalFile, added test for no double-loading ✏️ 🟢
/tmp/workspace/test/util/file.test.ts Added 14 comprehensive unit tests covering both new helper functions including edge cases, chained file references, variable rendering, and error handling ✏️ 🟡
/tmp/workspace/site/docs/providers/azure.md Added documentation for nested file references in schemas and variable rendering in file paths ✏️ 🟢
/tmp/workspace/site/docs/providers/openai.md Added documentation for nested file references with examples and explanation of use cases ✏️ 🟢
/tmp/workspace/site/docs/providers/xai.md Added documentation for loading schemas from external files and reference to OpenAI docs for nested references ✏️ 🟢
Architecture Impact
  • New Patterns: Consolidated helper functions for config file loading patterns, Specialized handler for response_format with nested schema loading
  • Coupling: Reduced code duplication by centralizing the renderVarsInObject + maybeLoadFromExternalFile pattern into reusable helpers, reducing coupling between providers and file loading utilities

Risk Areas: Azure Responses Provider behavior change: nested schema file references were silently failing before; now they are properly loaded. This could affect existing configurations that worked around this bug., File loading pattern changes across all three providers (Azure, OpenAI, xAI) - ensures consistent behavior but requires verification that all path resolution and variable rendering scenarios work correctly, Double-loading bug fix in OpenAI and xAI: previously nested schemas were loaded twice; now single load. Performance improvement but could expose edge cases if double-load had side effects

Suggestions
  • Verify that existing Azure customer configurations that relied on workarounds for the nested schema bug continue to work as expected
  • Run the mentioned end-to-end tests to ensure the consolidated helpers work correctly across all usage patterns
  • Consider adding integration tests for chained file references since this is a critical new capability

Full review in progress... | Powered by diffray

);

// Build text format configuration
let textFormat;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - Missing default textFormat when responseFormat is falsy
Agent: bugs

Category: bug

Description:
When config.response_format is falsy, textFormat remains undefined and is silently omitted from the request body. Azure and OpenAI providers both have an else clause to set textFormat to { format: { type: 'text' } } when responseFormat is undefined.

Suggestion:
Add an else clause after line 259 to initialize textFormat to a default text format when responseFormat is falsy: } else { textFormat = { format: { type: 'text' } }; }

Why this matters: Inconsistent API request format across providers may cause unexpected behavior.

Confidence: 90%
Rule: bug_null_pointer
Review ID: 9851952d-5055-47c1-b5e9-d7128d79d411
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

maybeLoadResponseFormatFromExternalFile as MockedFunction<
typeof maybeLoadResponseFormatFromExternalFile
>;
const _mockFs = fs as Mocked<typeof fs>;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Unused variable declaration
Agent: refactoring

Category: quality

Description:
_mockFs is declared but never used in the test file. The underscore prefix indicates intentional dead code that should be removed.

Suggestion:
Remove the unused variable declaration: const _mockFs = fs as Mocked;

Why this matters: Dead code adds confusion and maintenance burden.

Confidence: 85%
Rule: quality_unused_variable
Review ID: 9851952d-5055-47c1-b5e9-d7128d79d411
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

maybeLoadResponseFormatFromExternalFile(this.config.response_format, {});
} catch (error) {
throw new Error(
`Failed to load response_format file: ${this.config.response_format}\n` +

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Magic number for timeout without named constant
Agent: quality

Category: quality

Description:
Hard-coded timeout value 600_000 is duplicated from openai/responses.ts without a shared constant.

Suggestion:
Define a shared constant for this timeout value: const DEEP_RESEARCH_TIMEOUT_MS = 600_000;

Why this matters: Magic numbers obscure intent and make changes error-prone.

Confidence: 60%
Rule: qual_magic_numbers_js
Review ID: 9851952d-5055-47c1-b5e9-d7128d79d411
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines 18 to 20
const _mockMaybeLoadFromExternalFile = maybeLoadFromExternalFile as MockedFunction<
typeof maybeLoadFromExternalFile
>;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - Unused variable declaration
Agent: refactoring

Category: bug

Description:
_mockMaybeLoadFromExternalFile is declared with underscore prefix indicating intentionally unused, but variable serves no purpose and adds confusion. Grep confirms no other usages in the file.

Suggestion:
Remove the unused variable declaration on lines 18-20, keeping only mockMaybeLoadResponseFormatFromExternalFile which is used.

Confidence: 90%
Rule: quality_unused_variable
Review ID: 9851952d-5055-47c1-b5e9-d7128d79d411
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 44 issues: 12 kept, 32 filtered

Issues Found: 12

💬 See 4 individual line comment(s) for details.

📊 9 unique issue type(s) across 12 location(s)

📋 Full issue list (click to expand)

🟠 HIGH - Missing default textFormat when responseFormat is falsy

Agent: bugs

Category: bug

Why this matters: Inconsistent API request format across providers may cause unexpected behavior.

File: src/providers/xai/responses.ts:239

Description: When config.response_format is falsy, textFormat remains undefined and is silently omitted from the request body. Azure and OpenAI providers both have an else clause to set textFormat to { format: { type: 'text' } } when responseFormat is undefined.

Suggestion: Add an else clause after line 259 to initialize textFormat to a default text format when responseFormat is falsy: } else { textFormat = { format: { type: 'text' } }; }

Confidence: 90%

Rule: bug_null_pointer


🟠 HIGH - Missing null check on message before accessing properties

Agent: react

Category: bug

Why this matters: Prevents runtime TypeError crashes and makes fallback behavior explicit.

File: src/providers/azure/chat.ts:363-386

Description: Line 363 assigns message from choice?.message (which can be undefined), but lines 385-386 access message.tool_calls and message.function_call without null check.

Suggestion: Add null check: if (message) { const toolCalls = message.tool_calls; ... } or use optional chaining: message?.tool_calls

Confidence: 85%

Rule: ts_add_null_checks_before_accessing_propert


🟠 HIGH - Async function called without await

Agent: testing

Category: bug

File: test/util/file.test.ts:1078-1085

Description: Test calls maybeLoadToolsFromExternalFile() which is async (declared on line 459 of file.ts), but line 1082 does not await the result. Other tests in same file (lines 1075, 1090, 1098) correctly use await.

Suggestion: Add 'await' before the function call: 'await maybeLoadToolsFromExternalFile(tools, vars);' on line 1082

Confidence: 95%

Rule: test_js_async_not_awaited


🟠 HIGH - Unsafe any type for config parameter

Agent: typescript

Category: quality

File: src/util/file.ts:239-242

Description: Function maybeLoadConfigFromExternalFile uses any for both parameter and return type. This bypasses TypeScript's type checking for a recursive config processing function.

Suggestion: Use 'config: unknown' and return type 'unknown', or define a Config interface. Add runtime type guards for safe processing.

Confidence: 85%

Rule: ts_any_type_usage


🟡 MEDIUM - Environment variable pollution in parsePathOrGlob suite (2 occurrences)

Agent: microservices

Category: bug

Why this matters: Live I/O introduces slowness, nondeterminism, and external failures unrelated to the code.

📍 View all locations
File Description Suggestion Confidence
test/util/file.test.ts:1687-1697 The test at line 1689 sets process.env.FILE_PATH = 'file.txt' and deletes it after. If the test fail... Use a beforeEach/afterEach block for environment variable cleanup, or wrap in try-finally to ensure ... 70%
test/util/file.test.ts:1652-1659 The test at line 1653 sets process.env.PROMPTFOO_STRICT_FILES = 'true' and deletes it at line 1658. ... Use beforeEach/afterEach for environment variable management or wrap in try-finally to ensure cleanu... 70%

Rule: gen_no_live_io_in_unit_tests


🟡 MEDIUM - Duplicate file URL parsing logic

Agent: consistency

Category: quality

Why this matters: Duplicated code increases maintenance burden when fixing bugs or adding features.

File: src/providers/openai/chat.ts:78-121

Description: The loadExternalFunction method reimplements file:// URL parsing that already exists in src/util/functions/loadFunction.ts (parseFileUrl). While the implementations differ slightly, they solve the same problem.

Suggestion: Consider refactoring to use parseFileUrl from src/util/functions/loadFunction.ts for consistency, or document why different parsing logic is needed here.

Confidence: 65%

Rule: cons_duplicate_utility_function


🟡 MEDIUM - Magic number for timeout without named constant (2 occurrences)

Agent: quality

Category: quality

Why this matters: Magic numbers obscure intent and make changes error-prone.

📍 View all locations
File Description Suggestion Confidence
src/providers/openai/responses.ts:318 Hard-coded timeout value 600_000 is used without a named constant. Same value appears in azure/respo... Extract to a shared constant: const LONG_RUNNING_MODEL_TIMEOUT_MS = 600_000; 60%
src/providers/azure/responses.ts:235 Hard-coded timeout value 600_000 is duplicated from openai/responses.ts without a shared constant. Define a shared constant for this timeout value: const DEEP_RESEARCH_TIMEOUT_MS = 600_000; 60%

Rule: qual_magic_numbers_js


🟡 MEDIUM - Missing test coverage for context parameter

Agent: testing

Category: quality

File: test/providers/azure/responses.test.ts:105-248

Description: getAzureResponsesBody accepts context with vars (line 91 of responses.ts: context?: CallApiContextParams), and uses context?.vars on line 130. All tests call it with only the prompt argument, never testing variable substitution in response_format.

Suggestion: Add test case: await provider.getAzureResponsesBody('Hello', { vars: { schema_name: 'custom' } }) to verify variable rendering works correctly.

Confidence: 78%

Rule: test_new_parameter_coverage


🟡 MEDIUM - Unused variable declaration (2 occurrences)

Agent: refactoring

Category: quality

Why this matters: Dead code adds confusion and maintenance burden.

📍 View all locations
File Description Suggestion Confidence
test/providers/azure/responses.test.ts:25 _mockFs is declared but never used in the test file. The underscore prefix indicates intentional dea... Remove the unused variable declaration: const _mockFs = fs as Mocked; 85%
test/providers/azure/responses.test.ts:18-20 _mockMaybeLoadFromExternalFile is declared with underscore prefix indicating intentionally unused, b... Remove the unused variable declaration on lines 18-20, keeping only mockMaybeLoadResponseFormatFromE... 90%

Rule: quality_unused_variable


ℹ️ 8 issue(s) outside PR diff (click to expand)

These issues were found in lines not modified in this PR.

🟠 HIGH - Missing null check on message before accessing properties

Agent: react

Category: bug

Why this matters: Prevents runtime TypeError crashes and makes fallback behavior explicit.

File: src/providers/azure/chat.ts:363-386

Description: Line 363 assigns message from choice?.message (which can be undefined), but lines 385-386 access message.tool_calls and message.function_call without null check.

Suggestion: Add null check: if (message) { const toolCalls = message.tool_calls; ... } or use optional chaining: message?.tool_calls

Confidence: 85%

Rule: ts_add_null_checks_before_accessing_propert


🟠 HIGH - Async function called without await

Agent: testing

Category: bug

File: test/util/file.test.ts:1078-1085

Description: Test calls maybeLoadToolsFromExternalFile() which is async (declared on line 459 of file.ts), but line 1082 does not await the result. Other tests in same file (lines 1075, 1090, 1098) correctly use await.

Suggestion: Add 'await' before the function call: 'await maybeLoadToolsFromExternalFile(tools, vars);' on line 1082

Confidence: 95%

Rule: test_js_async_not_awaited


🟠 HIGH - Unsafe any type for config parameter

Agent: typescript

Category: quality

File: src/util/file.ts:239-242

Description: Function maybeLoadConfigFromExternalFile uses any for both parameter and return type. This bypasses TypeScript's type checking for a recursive config processing function.

Suggestion: Use 'config: unknown' and return type 'unknown', or define a Config interface. Add runtime type guards for safe processing.

Confidence: 85%

Rule: ts_any_type_usage


🟡 MEDIUM - Environment variable pollution in parsePathOrGlob suite (2 occurrences)

Agent: microservices

Category: bug

Why this matters: Live I/O introduces slowness, nondeterminism, and external failures unrelated to the code.

📍 View all locations
File Description Suggestion Confidence
test/util/file.test.ts:1687-1697 The test at line 1689 sets process.env.FILE_PATH = 'file.txt' and deletes it after. If the test fail... Use a beforeEach/afterEach block for environment variable cleanup, or wrap in try-finally to ensure ... 70%
test/util/file.test.ts:1652-1659 The test at line 1653 sets process.env.PROMPTFOO_STRICT_FILES = 'true' and deletes it at line 1658. ... Use beforeEach/afterEach for environment variable management or wrap in try-finally to ensure cleanu... 70%

Rule: gen_no_live_io_in_unit_tests


🟡 MEDIUM - Duplicate file URL parsing logic

Agent: consistency

Category: quality

Why this matters: Duplicated code increases maintenance burden when fixing bugs or adding features.

File: src/providers/openai/chat.ts:78-121

Description: The loadExternalFunction method reimplements file:// URL parsing that already exists in src/util/functions/loadFunction.ts (parseFileUrl). While the implementations differ slightly, they solve the same problem.

Suggestion: Consider refactoring to use parseFileUrl from src/util/functions/loadFunction.ts for consistency, or document why different parsing logic is needed here.

Confidence: 65%

Rule: cons_duplicate_utility_function


🟡 MEDIUM - Magic number for timeout without named constant

Agent: quality

Category: quality

Why this matters: Magic numbers obscure intent and make changes error-prone.

File: src/providers/openai/responses.ts:318

Description: Hard-coded timeout value 600_000 is used without a named constant. Same value appears in azure/responses.ts.

Suggestion: Extract to a shared constant: const LONG_RUNNING_MODEL_TIMEOUT_MS = 600_000;

Confidence: 60%

Rule: qual_magic_numbers_js


🟡 MEDIUM - Missing test coverage for context parameter

Agent: testing

Category: quality

File: test/providers/azure/responses.test.ts:105-248

Description: getAzureResponsesBody accepts context with vars (line 91 of responses.ts: context?: CallApiContextParams), and uses context?.vars on line 130. All tests call it with only the prompt argument, never testing variable substitution in response_format.

Suggestion: Add test case: await provider.getAzureResponsesBody('Hello', { vars: { schema_name: 'custom' } }) to verify variable rendering works correctly.

Confidence: 78%

Rule: test_new_parameter_coverage



Review ID: 9851952d-5055-47c1-b5e9-d7128d79d411
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

mldangelo and others added 2 commits January 2, 2026 15:14
…t constant

- Add missing else clause in xAI responses provider to set default text
  format when no response_format is specified (matches Azure/OpenAI behavior)
- Extract LONG_RUNNING_MODEL_TIMEOUT_MS constant to shared.ts
- Remove unused test mocks and imports in azure/responses.test.ts
- Simplify timeout logic with ternary operators

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chore: Move renderVarsInObject into Tools specific function

3 participants