import { Hono } from "hono" import { describeRoute, validator, resolver } from "hono-openapi" import z from "zod" import { Config } from "../../config/config" import { Provider } from "../../provider/provider" import { ModelsDev } from "../../provider/models" import { ProviderAuth } from "../../provider/auth" import { ProviderID } from "../../provider/schema" import { AppRuntime } from "../../effect/app-runtime" import { mapValues } from "remeda" import { errors } from "../error" import { lazy } from "../../util/lazy" import { Log } from "../../util/log" const log = Log.create({ service: "server" }) export const ProviderRoutes = lazy(() => new Hono() .get( "/", describeRoute({ summary: "List providers", description: "Get a list of all available AI providers, including both available and connected ones.", operationId: "provider.list", responses: { 200: { description: "List of providers", content: { "application/json": { schema: resolver( z.object({ all: Provider.Info.array(), default: z.record(z.string(), z.string()), connected: z.array(z.string()), }), ), }, }, }, }, }), async (c) => { const config = await Config.get() const disabled = new Set(config.disabled_providers ?? []) const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined const allProviders = await ModelsDev.get() const filteredProviders: Record = {} for (const [key, value] of Object.entries(allProviders)) { if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) { filteredProviders[key] = value } } const connected = await Provider.list() const providers = Object.assign( mapValues(filteredProviders, (x) => Provider.fromModelsDevProvider(x)), connected, ) return c.json({ all: Object.values(providers), default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id), connected: Object.keys(connected), }) }, ) .get( "/auth", describeRoute({ summary: "Get provider auth methods", description: "Retrieve available authentication methods for all AI providers.", operationId: "provider.auth", responses: { 200: { description: "Provider auth methods", content: { "application/json": { schema: resolver(z.record(z.string(), z.array(ProviderAuth.Method))), }, }, }, }, }), async (c) => { return c.json(await AppRuntime.runPromise(ProviderAuth.Service.use((svc) => svc.methods()))) }, ) .post( "/:providerID/oauth/authorize", describeRoute({ summary: "OAuth authorize", description: "Initiate OAuth authorization for a specific AI provider to get an authorization URL.", operationId: "provider.oauth.authorize", responses: { 200: { description: "Authorization URL and method", content: { "application/json": { schema: resolver(ProviderAuth.Authorization.optional()), }, }, }, ...errors(400), }, }), validator( "param", z.object({ providerID: ProviderID.zod.meta({ description: "Provider ID" }), }), ), validator( "json", z.object({ method: z.number().meta({ description: "Auth method index" }), inputs: z.record(z.string(), z.string()).optional().meta({ description: "Prompt inputs" }), }), ), async (c) => { const providerID = c.req.valid("param").providerID const { method, inputs } = c.req.valid("json") const result = await AppRuntime.runPromise( ProviderAuth.Service.use((svc) => svc.authorize({ providerID, method, inputs, }), ), ) return c.json(result) }, ) .post( "/:providerID/oauth/callback", describeRoute({ summary: "OAuth callback", description: "Handle the OAuth callback from a provider after user authorization.", operationId: "provider.oauth.callback", responses: { 200: { description: "OAuth callback processed successfully", content: { "application/json": { schema: resolver(z.boolean()), }, }, }, ...errors(400), }, }), validator( "param", z.object({ providerID: ProviderID.zod.meta({ description: "Provider ID" }), }), ), validator( "json", z.object({ method: z.number().meta({ description: "Auth method index" }), code: z.string().optional().meta({ description: "OAuth authorization code" }), }), ), async (c) => { const providerID = c.req.valid("param").providerID const { method, code } = c.req.valid("json") await AppRuntime.runPromise( ProviderAuth.Service.use((svc) => svc.callback({ providerID, method, code, }), ), ) return c.json(true) }, ), )