chore: kanban template
This commit is contained in:
99
packages/functions/src/services.ts
Normal file
99
packages/functions/src/services.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
JsonConsoleLogger,
|
||||
LocalSecretService,
|
||||
LocalVariablesService,
|
||||
InMemoryQueueService,
|
||||
} from '@pikku/core/services'
|
||||
import { pikkuServices } from '../.pikku/pikku-types.gen.js'
|
||||
import { TypedSecretService } from '../.pikku/secrets/pikku-secrets.gen.js'
|
||||
import { TypedVariablesService } from '../.pikku/variables/pikku-variables.gen.js'
|
||||
import { CFWorkerSchemaService } from '@pikku/schema-cfworker'
|
||||
import { Kysely, CamelCasePlugin } from 'kysely'
|
||||
import { LibsqlWebDialect } from '@pikku/kysely-sqlite'
|
||||
import type { VercelAIAgentRunner } from '@pikku/ai-vercel'
|
||||
import type { DB } from './types/db.types.js'
|
||||
|
||||
export const createSingletonServices = pikkuServices(async (config, existingServices) => {
|
||||
const variables =
|
||||
existingServices?.variables ?? new TypedVariablesService(new LocalVariablesService())
|
||||
const secrets =
|
||||
existingServices?.secrets ?? new TypedSecretService(new LocalSecretService(variables))
|
||||
const logger = existingServices?.logger ?? new JsonConsoleLogger()
|
||||
const schema = existingServices?.schema ?? new CFWorkerSchemaService(logger)
|
||||
|
||||
// In CF Workers and containers DATABASE_URL is injected by fabric.
|
||||
// In local dev (pikku dev) existingServices.kysely is the node:sqlite instance
|
||||
// set up by the CLI — prefer it over DATABASE_URL so sandbox mode always uses
|
||||
// the local migrated SQLite rather than the remote Turso URL from the cascade.
|
||||
// In tests existingServices.kysely is provided by the test support layer.
|
||||
// node:sqlite / createNodeSqliteKysely is intentionally absent — it is not
|
||||
// available in CF Workers. See tests/support/services.ts for the dev path.
|
||||
let kysely: Kysely<DB> | undefined
|
||||
if (existingServices?.kysely) {
|
||||
kysely = existingServices.kysely as Kysely<DB>
|
||||
} else {
|
||||
const databaseUrl = await variables.get('DATABASE_URL')
|
||||
if (databaseUrl) {
|
||||
if (/^postgres(ql)?:\/\//.test(databaseUrl)) {
|
||||
const [{ PostgresJSDialect }, postgres] = await Promise.all([
|
||||
import('kysely-postgres-js'),
|
||||
import('postgres'),
|
||||
])
|
||||
kysely = new Kysely<DB>({
|
||||
dialect: new PostgresJSDialect({
|
||||
postgres: postgres.default(databaseUrl),
|
||||
}),
|
||||
plugins: [new CamelCasePlugin()],
|
||||
})
|
||||
} else {
|
||||
kysely = new Kysely<DB>({
|
||||
dialect: new LibsqlWebDialect({ url: databaseUrl }),
|
||||
plugins: [new CamelCasePlugin()],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'kysely service not provided: set DATABASE_URL (Postgres or libsql) or pass kysely via existingServices',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const litellmProxyUrl = process.env.LITELLM_PROXY_URL ?? null
|
||||
const litellmApiKey = process.env.LITELLM_API_KEY ?? null
|
||||
let aiAgentRunner: VercelAIAgentRunner | undefined
|
||||
if (litellmProxyUrl && litellmApiKey) {
|
||||
// The AI SDKs (~3MB) are stubbed out of non-agent units at bundle time —
|
||||
// only units with the `ai-model` capability keep them. So import them
|
||||
// dynamically and guard on the module resolving to a real export; in a
|
||||
// stubbed unit the import yields `{}` and the runner is simply not built.
|
||||
const aiVercel = await import('@pikku/ai-vercel')
|
||||
const aiSdk = await import('@ai-sdk/openai-compatible')
|
||||
if (aiVercel.VercelAIAgentRunner && aiSdk.createOpenAICompatible) {
|
||||
const litellmProvider = aiSdk.createOpenAICompatible({
|
||||
name: 'litellm',
|
||||
baseURL: litellmProxyUrl,
|
||||
apiKey: litellmApiKey,
|
||||
})
|
||||
aiAgentRunner = new aiVercel.VercelAIAgentRunner({
|
||||
openai: (modelId: string) => litellmProvider.chatModel(modelId),
|
||||
anthropic: (modelId: string) => litellmProvider.chatModel(modelId),
|
||||
google: (modelId: string) => litellmProvider.chatModel(modelId),
|
||||
deepseek: (modelId: string) => litellmProvider.chatModel(modelId),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const queueService = existingServices?.queueService ?? new InMemoryQueueService()
|
||||
|
||||
return {
|
||||
...(existingServices ?? {}),
|
||||
config,
|
||||
variables,
|
||||
secrets,
|
||||
logger,
|
||||
schema,
|
||||
kysely,
|
||||
queueService,
|
||||
...(aiAgentRunner ? { aiAgentRunner } : {}),
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user