feat(connectors): add Ollama and LM Studio as a local auth kind

Both servers expose OpenAI-compatible /v1/chat/completions and /v1/models,
so we route them through pi-ai's `openai-completions` API the same way
Fireworks does — but with a per-installation base URL the user enters
during onboarding instead of a hardcoded one. The shared schemas grow a
`local` credential payload carrying just the baseUrl; the credential
resolver surfaces it on `ResolvedCredential.baseUrl`, the runtime bridge
forwards it through `prepareRun`, and the worker plumbs it into
`resolveModel` so the streamed Model<openai-completions> targets the
user's server. The local-cost path short-circuits to zero in cost-writer
since users bill their own hardware, and the live model list is fetched
from the saved baseUrl by `fetchLocalConnectorModels` (Ollama and LM
Studio both speak the standard OpenAI catalog shape). Onboarding ships
a new LocalProviderCard and the Settings panel a LocalForm; both default
to the catalog's per-connector localhost URL and accept any custom URL.
This commit is contained in:
ZacharyZhang-NY committed 2026-05-14 14:55:38 -04:00
1 parent 07d9c00fab
commit a532120649
26 files changed
+504 -18

No files matched your search

+3
View File
@@ -49,6 +49,9 @@ export const connectorIdEnum = z.enum([
"amazon-bedrock",
"azure-openai",
"google-vertex",
// Local (2)
"ollama",
"lm-studio",
]);
export type ConnectorId = z.infer<typeof connectorIdEnum>;
@@ -6,6 +6,7 @@ export const credentialKindSchema = z.enum([
"aws",
"azure",
"gcp_adc",
"local",
]);
export const credentialStatusSchema = z.enum([
@@ -49,11 +50,16 @@ export const gcpAdcCredentialSchema = z.object({
credentialsPath: z.string().optional(),
});
export const localCredentialSchema = z.object({
baseUrl: z.string().url(),
});
export const credentialPayloadSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("api_key"), value: apiKeyValueSchema }),
z.object({ kind: z.literal("azure"), value: azureCredentialSchema }),
z.object({ kind: z.literal("aws"), value: awsCredentialSchema }),
z.object({ kind: z.literal("gcp_adc"), value: gcpAdcCredentialSchema }),
z.object({ kind: z.literal("local"), value: localCredentialSchema }),
]);
export const saveCredentialInputSchema = z.object({
@@ -85,4 +91,5 @@ export type SaveCredentialInput = z.infer<typeof saveCredentialInputSchema>;
export type AwsCredential = z.infer<typeof awsCredentialSchema>;
export type AzureCredential = z.infer<typeof azureCredentialSchema>;
export type GcpAdcCredential = z.infer<typeof gcpAdcCredentialSchema>;
export type LocalCredential = z.infer<typeof localCredentialSchema>;
export type ApiKeyValue = z.infer<typeof apiKeyValueSchema>;