Skip to content

feat: move mfb provider from azure to pair foundry#9426

Draft
kevin9foong wants to merge 9 commits into
developfrom
feat/mfb-pair-foundry
Draft

feat: move mfb provider from azure to pair foundry#9426
kevin9foong wants to merge 9 commits into
developfrom
feat/mfb-pair-foundry

Conversation

@kevin9foong
Copy link
Copy Markdown
Contributor

Problem and solution

Shift from azure AI foundry to pair foundry

This should provide the following benefits:

  • latest models, option to use structured outputs to reduce flakiness in a subsequent PR.
  • reduce FormSG team's need to maintain security policies (Eg, no logging agreeements)
  • reduce need for individual financial procurement

Closes [insert issue #]

Breaking Changes

No - this PR is backwards compatible

Tests

Deploy Notes

New environment variables:

  • env var : env var details

  • dependency : dependency details
    ai-sdk, this is the recommended approach by the pair team. for interoperabilty, the @ai-sdk/openai provider also works with any OpenAI-compatible API we wish to use in the future.

@kevin9foong kevin9foong requested a review from a team as a code owner May 15, 2026 03:03
@kevin9foong kevin9foong marked this pull request as draft May 16, 2026 16:18
@kevin9foong kevin9foong force-pushed the feat/mfb-pair-foundry branch from 6668595 to 79b6906 Compare May 26, 2026 02:22
kevin9foong and others added 9 commits May 26, 2026 10:24
Adds the Vercel AI SDK (`ai`) to the backend so the MFB LLM client can
be migrated off the Azure OpenAI client onto `@ai-sdk/openai`'s
`createOpenAI` provider pointed at Pair Foundry's PX Engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the Azure OpenAI client in `ai-model.ts` with a Vercel AI SDK
wrapper built on `createOpenAI` (`@ai-sdk/openai`) plus `generateText`
(`ai`), targeting Pair Foundry's PX Engine via the existing
`aisdkConfig` (providerName, apiKey, baseUrl, modelName from env/SSM).

Behaviour-preserving from the caller's perspective:

- `sendPromptToModel({ messages, options, formId })` keeps its
  signature and `ResultAsync<string | null, ...>` return type.
- Provider/client construction failures still surface as
  `ModelGetClientFailureError`; request failures as
  `ModelResponseFailureError` (with `formId` in log meta); empty/missing
  text in the model response still returns `null`.
- `temperature` stays unpinned; callers control it via `options`.

Internal type and shape changes:

- `Message` is now ai-sdk's `ModelMessage`; the local `Role` enum is
  removed (callers use the string literals `'user'`/`'system'`).
- Vision-flow image parts move from the OpenAI shape
  `{ type: 'image_url', image_url: { url } }` to ai-sdk's
  `{ type: 'image', image: <dataURL> }`.
- JSON-mode is plumbed per-call via
  `options.providerOptions.openai.responseFormat`, replacing the
  OpenAI-SDK-native `response_format` option. Downstream `JSON.parse`
  + zod validation in the assistance service is unchanged.

Existing `admin-form.assistance.service.spec.ts` continues to mock
`sendPromptToModel` wholesale and passes unchanged through the swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a wrapper-level spec for `sendPromptToModel` that mocks the
ai-sdk boundary (`generateText`, `createOpenAI`) rather than going
deeper, so it stays valid if internals are later refactored (e.g.
swapping `generateText` for `generateObject` in the structured-outputs
follow-up).

Covers:

- Happy path: messages forwarded to `generateText` with the model
  returned by the OpenAI provider, and `result.text` is returned.
- Null response: `generateText` resolves with empty `text` → wrapper
  returns `ok(null)`.
- Client construction failure: `createOpenAI` throws → wrapper
  returns `err(ModelGetClientFailureError)` and never calls
  `generateText`.
- Request failure: `generateText` rejects → wrapper returns
  `err(ModelResponseFailureError)` and logs include `formId` in meta.
- Options pass-through: caller-supplied `temperature` and
  `providerOptions.openai.responseFormat` reach `generateText`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kevin9foong kevin9foong force-pushed the feat/mfb-pair-foundry branch from 79b6906 to d37c0cd Compare May 26, 2026 02:24
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.

1 participant