use rowboat model gateway when logged in

This commit is contained in:
Ramnique Singh 2026-03-06 14:37:34 +05:30
parent bd6ef0df28
commit 7311501343
8 changed files with 121 additions and 6 deletions

View file

@ -0,0 +1,8 @@
import container from '../di/container.js';
import { IOAuthRepo } from '../auth/repo.js';
export async function isSignedIn(): Promise<boolean> {
const oauthRepo = container.resolve<IOAuthRepo>('oauthRepo');
const { tokens } = await oauthRepo.read('rowboat');
return !!tokens;
}

View file

@ -15,6 +15,8 @@ import { isBlocked, extractCommandNames } from "../application/lib/command-execu
import container from "../di/container.js";
import { IModelConfigRepo } from "../models/repo.js";
import { createProvider } from "../models/models.js";
import { isSignedIn } from "../account/account.js";
import { getGatewayProvider } from "../models/gateway.js";
import { IAgentsRepo } from "./repo.js";
import { IMonotonicallyIncreasingIdGenerator } from "../application/lib/id-gen.js";
import { IBus } from "../application/lib/bus.js";
@ -768,7 +770,9 @@ export async function* streamAgent({
const tools = await buildTools(agent);
// set up provider + model
const provider = createProvider(modelConfig.provider);
const provider = await isSignedIn()
? await getGatewayProvider()
: createProvider(modelConfig.provider);
const knowledgeGraphAgents = ["note_creation", "email-draft", "meeting-prep", "labeling_agent", "note_tagging_agent"];
const modelId = (knowledgeGraphAgents.includes(state.agentName!) && modelConfig.knowledgeGraphModel)
? modelConfig.knowledgeGraphModel

View file

@ -16,6 +16,8 @@ import type { ToolContext } from "./exec-tool.js";
import { generateText } from "ai";
import { createProvider } from "../../models/models.js";
import { IModelConfigRepo } from "../../models/repo.js";
import { isSignedIn } from "../../account/account.js";
import { getGatewayProvider } from "../../models/gateway.js";
// Parser libraries are loaded dynamically inside parseFile.execute()
// to avoid pulling pdfjs-dist's DOM polyfills into the main bundle.
// Import paths are computed so esbuild cannot statically resolve them.
@ -632,7 +634,9 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
// Resolve model config from DI container
const modelConfigRepo = container.resolve<IModelConfigRepo>('modelConfigRepo');
const modelConfig = await modelConfigRepo.getConfig();
const provider = createProvider(modelConfig.provider);
const provider = await isSignedIn()
? await getGatewayProvider()
: createProvider(modelConfig.provider);
const model = provider.languageModel(modelConfig.model);
const userPrompt = prompt || 'Convert this file to well-structured markdown.';

View file

@ -1,6 +1,5 @@
import { z } from 'zod';
const SUPABASE_PROJECT_URL = 'http://127.0.0.1:54321';
import { SUPABASE_PROJECT_URL } from '../config/env.js';
/**
* Discovery configuration - how to get OAuth endpoints
@ -56,7 +55,7 @@ const providerConfigs: ProviderConfig = {
rowboat: {
discovery: {
mode: 'issuer',
issuer: `${SUPABASE_PROJECT_URL}/.well-known/oauth-authorization-server`,
issuer: `${SUPABASE_PROJECT_URL}/auth/v1/.well-known/oauth-authorization-server`,
},
client: {
mode: 'dcr',

View file

@ -0,0 +1,5 @@
export const ROWBOAT_AI_GATEWAY_BASE_URL =
process.env.ROWBOAT_AI_GATEWAY_BASE_URL || 'http://localhost:3002/v1';
export const SUPABASE_PROJECT_URL =
process.env.SUPABASE_PROJECT_URL || 'http://127.0.0.1:54321';

View file

@ -0,0 +1,86 @@
import { ProviderV2 } from '@ai-sdk/provider';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import container from '../di/container.js';
import { IOAuthRepo } from '../auth/repo.js';
import { IClientRegistrationRepo } from '../auth/client-repo.js';
import { getProviderConfig } from '../auth/providers.js';
import * as oauthClient from '../auth/oauth-client.js';
import { ROWBOAT_AI_GATEWAY_BASE_URL } from '../config/env.js';
async function getAccessToken(): Promise<string> {
const oauthRepo = container.resolve<IOAuthRepo>('oauthRepo');
const { tokens } = await oauthRepo.read('rowboat');
if (!tokens) {
throw new Error('Not signed into Rowboat');
}
if (!oauthClient.isTokenExpired(tokens)) {
return tokens.access_token;
}
if (!tokens.refresh_token) {
throw new Error('Rowboat token expired and no refresh token available. Please sign in again.');
}
const providerConfig = getProviderConfig('rowboat');
if (providerConfig.discovery.mode !== 'issuer') {
throw new Error('Rowboat provider requires issuer discovery mode');
}
const clientRepo = container.resolve<IClientRegistrationRepo>('clientRegistrationRepo');
const registration = await clientRepo.getClientRegistration('rowboat');
if (!registration) {
throw new Error('Rowboat client not registered. Please sign in again.');
}
const config = await oauthClient.discoverConfiguration(
providerConfig.discovery.issuer,
registration.client_id,
);
const refreshed = await oauthClient.refreshTokens(
config,
tokens.refresh_token,
tokens.scopes,
);
await oauthRepo.upsert('rowboat', { tokens: refreshed });
return refreshed.access_token;
}
export async function getGatewayProvider(): Promise<ProviderV2> {
const accessToken = await getAccessToken();
return createOpenRouter({
baseURL: ROWBOAT_AI_GATEWAY_BASE_URL,
apiKey: accessToken,
});
}
type ProviderSummary = {
id: string;
name: string;
models: Array<{
id: string;
name?: string;
release_date?: string;
}>;
};
export async function listGatewayModels(): Promise<{ providers: ProviderSummary[] }> {
const accessToken = await getAccessToken();
const response = await fetch(`${ROWBOAT_AI_GATEWAY_BASE_URL}/models`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok) {
throw new Error(`Gateway /v1/models failed: ${response.status}`);
}
const body = await response.json() as { data: Array<{ id: string }> };
const models = body.data.map((m) => ({ id: m.id }));
return {
providers: [{
id: 'rowboat',
name: 'Rowboat',
models,
}],
};
}

View file

@ -8,6 +8,8 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { LlmModelConfig, LlmProvider } from "@x/shared/dist/models.js";
import z from "zod";
import { isSignedIn } from "../account/account.js";
import { getGatewayProvider } from "./gateway.js";
export const Provider = LlmProvider;
export const ModelConfig = LlmModelConfig;
@ -78,7 +80,9 @@ export async function testModelConnection(
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), effectiveTimeout);
try {
const provider = createProvider(providerConfig);
const provider = await isSignedIn()
? await getGatewayProvider()
: createProvider(providerConfig);
const languageModel = provider.languageModel(model);
await generateText({
model: languageModel,