mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
refactor(http): organize shared route package
This commit is contained in:
1 parent
bf7004d0e0
commit
4b52a7d2f8
35 files changed
+84
-217
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { describe, it, beforeEach } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { setAuthToken, setRequireAuth, authHook } from '@openchatlab/http-routes/auth'
|
||||
import { setAuthToken, setRequireAuth, authHook } from './auth'
|
||||
|
||||
function fakeRequest(url: string, authorization?: string) {
|
||||
return { url, headers: { authorization } } as never
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/** CLI HTTP authentication policy built on the shared Bearer hook. */
|
||||
|
||||
import { createBearerAuthHook } from '@openchatlab/http-routes/auth'
|
||||
|
||||
let cachedToken: string | null = null
|
||||
let requireAuthEnabled = false
|
||||
|
||||
export function setAuthToken(token: string): void {
|
||||
cachedToken = token
|
||||
}
|
||||
|
||||
/** Require Bearer auth for Web UI routes in headless or remote deployments. */
|
||||
export function setRequireAuth(enabled: boolean): void {
|
||||
requireAuthEnabled = enabled
|
||||
}
|
||||
|
||||
export const authHook = createBearerAuthHook({
|
||||
getToken: () => cachedToken,
|
||||
shouldAuthenticate: (request) =>
|
||||
request.url.startsWith('/api/') || (requireAuthEnabled && request.url.startsWith('/_web/')),
|
||||
})
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
CustomProviderStore,
|
||||
CustomModelStore,
|
||||
createFileConfigStorage,
|
||||
createAuthProfileLlmConfigStore,
|
||||
applyPendingNodeDataDirMigrationIfNeeded,
|
||||
hasPendingElectronDataWarning,
|
||||
verifyCliDataPath,
|
||||
@@ -27,8 +28,7 @@ import {
|
||||
} from '@openchatlab/node-runtime'
|
||||
import type { SemanticIndexRuntime } from '@openchatlab/node-runtime'
|
||||
import { createServer } from './server'
|
||||
import { setAuthToken, setRequireAuth } from '@openchatlab/http-routes/auth'
|
||||
import { createAuthProfileLlmConfigStore } from '@openchatlab/http-routes/ai-config'
|
||||
import { setAuthToken, setRequireAuth } from './auth'
|
||||
import { registerWebRoutes } from './routes/web'
|
||||
import { registerProxyRoutes } from './routes/proxy'
|
||||
import { initServerAiLogger, closeServerAiLogger } from '../ai/logger'
|
||||
|
||||
@@ -3,20 +3,9 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
import type { DatabaseManager } from '@openchatlab/node-runtime'
|
||||
import { resolveCliPath } from '../../../paths'
|
||||
|
||||
export function parseTimeFilter(query: Record<string, string | undefined>): TimeFilter | undefined {
|
||||
const { startTs, endTs, memberId } = query
|
||||
if (!startTs && !endTs && !memberId) return undefined
|
||||
const filter: TimeFilter = {}
|
||||
if (startTs) filter.startTs = parseInt(startTs, 10)
|
||||
if (endTs) filter.endTs = parseInt(endTs, 10)
|
||||
if (memberId) filter.memberId = parseInt(memberId, 10)
|
||||
return filter
|
||||
}
|
||||
|
||||
export function resolveNativeBinding(): string | undefined {
|
||||
if (process.versions.electron) return undefined
|
||||
const nativePath = resolveCliPath('native/better_sqlite3.node')
|
||||
@@ -31,11 +20,3 @@ export function getAiDataDir(dbManager: DatabaseManager): string {
|
||||
}
|
||||
return pathProvider.getAiDataDir()
|
||||
}
|
||||
|
||||
export function ensureDb(dbManager: DatabaseManager, sessionId: string) {
|
||||
const db = dbManager.open(sessionId)
|
||||
if (!db) {
|
||||
throw Object.assign(new Error(`Session not found: ${sessionId}`), { statusCode: 404 })
|
||||
}
|
||||
return db
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import Fastify from 'fastify'
|
||||
import type { SessionRuntimeAdapter } from '@openchatlab/node-runtime'
|
||||
import type { HttpRouteContext } from '@openchatlab/http-routes'
|
||||
import { registerSessionRoutes } from '@openchatlab/http-routes'
|
||||
|
||||
function createMissingSessionAdapter(): SessionRuntimeAdapter {
|
||||
return {
|
||||
listSessionIds: () => [],
|
||||
openReadonly: () => null,
|
||||
openWritable: () => null,
|
||||
closeSession: () => {},
|
||||
getDbPath: (sessionId) => `/tmp/${sessionId}.db`,
|
||||
deleteSessionFile: () => false,
|
||||
ensureReadonly: () => {
|
||||
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
||||
},
|
||||
ensureWritable: () => {
|
||||
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createMockContext(adapter: SessionRuntimeAdapter): HttpRouteContext {
|
||||
return {
|
||||
dbManager: {} as any,
|
||||
sessionAdapter: adapter,
|
||||
pathProvider: {} as any,
|
||||
getVersion: () => '0.0.0-test',
|
||||
}
|
||||
}
|
||||
|
||||
describe('shared session routes', () => {
|
||||
it('returns 404 when requesting a missing session by id', async () => {
|
||||
const app = Fastify()
|
||||
const ctx = createMockContext(createMissingSessionAdapter())
|
||||
registerSessionRoutes(app, ctx)
|
||||
|
||||
const response = await app.inject({ method: 'GET', url: '/_web/sessions/missing' })
|
||||
await app.close()
|
||||
|
||||
assert.equal(response.statusCode, 404)
|
||||
})
|
||||
})
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { authHook } from '@openchatlab/http-routes/auth'
|
||||
import { createApiServer } from '@openchatlab/http-routes/server'
|
||||
import { appLogger } from '@openchatlab/node-runtime'
|
||||
import { authHook } from './auth'
|
||||
|
||||
export function createServer(): FastifyInstance {
|
||||
return createApiServer({
|
||||
|
||||
@@ -8,11 +8,10 @@ const rootDir = resolve(__dirname, '../..')
|
||||
const rootPkg = JSON.parse(readFileSync(resolve(rootDir, 'package.json'), 'utf-8'))
|
||||
const appVersion: string = rootPkg.version
|
||||
const openChatLabAliases = {
|
||||
'@openchatlab/http-routes/ai-config': resolve(rootDir, 'packages/http-routes/src/ai-config.ts'),
|
||||
'@openchatlab/http-routes/auth': resolve(rootDir, 'packages/http-routes/src/auth.ts'),
|
||||
'@openchatlab/http-routes/errors': resolve(rootDir, 'packages/http-routes/src/errors.ts'),
|
||||
'@openchatlab/http-routes/import': resolve(rootDir, 'packages/http-routes/src/import.ts'),
|
||||
'@openchatlab/http-routes/rest': resolve(rootDir, 'packages/http-routes/src/rest.ts'),
|
||||
'@openchatlab/http-routes/import': resolve(rootDir, 'packages/http-routes/src/import/json-push-handler.ts'),
|
||||
'@openchatlab/http-routes/rest': resolve(rootDir, 'packages/http-routes/src/routes/rest/index.ts'),
|
||||
'@openchatlab/http-routes/server': resolve(rootDir, 'packages/http-routes/src/server.ts'),
|
||||
'@openchatlab/node-runtime/import/archive/source-manager': resolve(
|
||||
rootDir,
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
CustomProviderStore,
|
||||
CustomModelStore,
|
||||
createFileConfigStorage,
|
||||
createAuthProfileLlmConfigStore,
|
||||
MergeSessionCache,
|
||||
withDataDirImportLock,
|
||||
raiseChatDbCompatibilityGate,
|
||||
@@ -30,7 +31,6 @@ import type { StreamImportDeps, SemanticIndexRuntime } from '@openchatlab/node-r
|
||||
import { getLoadablePath as getSqliteVecLoadablePath } from 'sqlite-vec'
|
||||
import multipart from '@fastify/multipart'
|
||||
import { registerSharedRoutes, registerAutomationRoutes } from '@openchatlab/http-routes'
|
||||
import { createAuthProfileLlmConfigStore } from '@openchatlab/http-routes/ai-config'
|
||||
import { createApiServer } from '@openchatlab/http-routes/server'
|
||||
import type { HttpRouteContext } from '@openchatlab/http-routes'
|
||||
import { reloadTimer, stopTimer, type DataSourceManager, type PullEngine } from '@openchatlab/sync'
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
"name": "@openchatlab/http-routes",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "ChatLab shared HTTP API routes — consumed by CLI Server and Electron Internal Server",
|
||||
"description": "ChatLab shared HTTP routes and infrastructure for CLI and Electron servers",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./ai-config": "./src/ai-config.ts",
|
||||
"./auth": "./src/auth.ts",
|
||||
"./errors": "./src/errors.ts",
|
||||
"./import": "./src/import.ts",
|
||||
"./rest": "./src/rest.ts",
|
||||
"./import": "./src/import/json-push-handler.ts",
|
||||
"./rest": "./src/routes/rest/index.ts",
|
||||
"./server": "./src/server.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { createAuthProfileLlmConfigStore, type AuthProfileLlmConfigStoreDeps } from './ai/auth-profile-llm-config-store'
|
||||
@@ -1,18 +1,13 @@
|
||||
/**
|
||||
* ChatLab HTTP API — Bearer Token authentication hook
|
||||
*
|
||||
* Shared auth middleware for CLI Server and Electron APIs.
|
||||
* URL classification: /api/* always requires token, /_web/* conditionally,
|
||||
* static files and SPA fallback are public.
|
||||
* Shared auth middleware factory for CLI and Electron APIs.
|
||||
*/
|
||||
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify'
|
||||
import { timingSafeEqual, createHmac, randomBytes } from 'crypto'
|
||||
import { unauthorized, errorResponse } from './errors'
|
||||
|
||||
let cachedToken: string | null = null
|
||||
let requireAuthEnabled = false
|
||||
|
||||
export interface BearerAuthOptions {
|
||||
getToken: () => string | null | undefined
|
||||
shouldAuthenticate?: (request: FastifyRequest) => boolean
|
||||
@@ -21,18 +16,6 @@ export interface BearerAuthOptions {
|
||||
invalidTokenMessage?: string
|
||||
}
|
||||
|
||||
export function setAuthToken(token: string): void {
|
||||
cachedToken = token
|
||||
}
|
||||
|
||||
/**
|
||||
* When enabled, /_web/* routes also require Bearer token (same as /api/*).
|
||||
* Used for server/headless deployments where same-origin assumption doesn't hold.
|
||||
*/
|
||||
export function setRequireAuth(enabled: boolean): void {
|
||||
requireAuthEnabled = enabled
|
||||
}
|
||||
|
||||
export function createBearerAuthHook(options: BearerAuthOptions) {
|
||||
// Compare via HMAC digests (fixed 32-byte length) to avoid leaking token length.
|
||||
const hmacKey = randomBytes(32)
|
||||
@@ -62,9 +45,3 @@ export function createBearerAuthHook(options: BearerAuthOptions) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const authHook = createBearerAuthHook({
|
||||
getToken: () => cachedToken,
|
||||
shouldAuthenticate: (request) =>
|
||||
request.url.startsWith('/api/') || (requireAuthEnabled && request.url.startsWith('/_web/')),
|
||||
})
|
||||
@@ -30,7 +30,7 @@ import type {
|
||||
GlobalInsightService,
|
||||
} from '@openchatlab/node-runtime'
|
||||
import type { RuntimeIdentity } from '@openchatlab/node-runtime'
|
||||
import type { RestSessionProvider } from './routes/rest-session-provider'
|
||||
import type { RestSessionProvider } from './routes/rest/session-provider'
|
||||
|
||||
export interface HttpRouteContext {
|
||||
dbManager: DatabaseManager
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export { registerImportRoutes } from './routes/imports'
|
||||
export {
|
||||
buildImportIdempotencyCacheKey,
|
||||
createJsonPushImportHandler,
|
||||
type ImportSuccessResponse,
|
||||
type JsonPushImportHandler,
|
||||
type JsonPushImportHandlerOptions,
|
||||
type JsonPushImportHttpResult,
|
||||
type JsonPushImportRequest,
|
||||
} from './import/json-push-handler'
|
||||
@@ -8,75 +8,6 @@ export type {
|
||||
} from './context'
|
||||
export { registerSharedRoutes } from './register'
|
||||
export type { SharedRouteOptions } from './register'
|
||||
export { setAuthToken, setRequireAuth, authHook } from './auth'
|
||||
export {
|
||||
ApiError,
|
||||
ApiErrorCode,
|
||||
unauthorized,
|
||||
sessionNotFound,
|
||||
invalidFormat,
|
||||
invalidPayload,
|
||||
sqlReadonlyViolation,
|
||||
sqlExecutionError,
|
||||
exportTooLarge,
|
||||
serverError,
|
||||
importInProgress,
|
||||
idempotencyConflict,
|
||||
idempotencyPending,
|
||||
importFailed,
|
||||
dataDirIncompatible,
|
||||
apiErrorFromUnknown,
|
||||
successResponse,
|
||||
errorResponse,
|
||||
} from './errors'
|
||||
export { parseTimeFilter } from './helpers'
|
||||
export { createApiServer, configureApiErrorHandler, DEFAULT_JSON_BODY_LIMIT } from './server'
|
||||
export type { ApiServerOptions } from './server'
|
||||
export {
|
||||
buildImportIdempotencyCacheKey,
|
||||
createJsonPushImportHandler,
|
||||
type ImportSuccessResponse,
|
||||
type JsonPushImportHandler,
|
||||
type JsonPushImportHandlerOptions,
|
||||
type JsonPushImportHttpResult,
|
||||
type JsonPushImportRequest,
|
||||
} from './import/json-push-handler'
|
||||
export { buildAnalyticsCacheKey, withAnalyticsCache } from './analytics-cache'
|
||||
export { executeRegistryTool } from './ai/tool-executor'
|
||||
export type { AiToolExecutionDeps } from './ai/tool-executor'
|
||||
export { createAuthProfileLlmConfigStore } from './ai/auth-profile-llm-config-store'
|
||||
export type { AuthProfileLlmConfigStoreDeps } from './ai/auth-profile-llm-config-store'
|
||||
|
||||
// Individual route registration for granular testing or selective registration
|
||||
export { registerSystemRoutes } from './routes/system'
|
||||
export type { SystemRouteContext } from './routes/system'
|
||||
export { registerRestSessionRoutes } from './routes/sessions'
|
||||
export { createDatabaseRestSessionProvider } from './routes/rest-session-provider'
|
||||
export type {
|
||||
RestMessageQuery,
|
||||
RestMessagePage,
|
||||
RestSessionDetail,
|
||||
RestSessionExportData,
|
||||
RestSessionOverview,
|
||||
RestSessionProvider,
|
||||
RestSessionSummary,
|
||||
} from './routes/rest-session-provider'
|
||||
export { registerSessionRoutes } from './routes/web/sessions'
|
||||
export { registerMemberRoutes } from './routes/web/members'
|
||||
export { registerPreferencesRoutes } from './routes/web/preferences'
|
||||
export { registerAnalyticsRoutes } from './routes/web/analytics'
|
||||
export { registerSqlRoutes } from './routes/web/sql'
|
||||
export { registerSessionIndexRoutes } from './routes/web/session-index'
|
||||
export { registerExportRoutes } from './routes/web/export'
|
||||
export { registerNlpRoutes } from './routes/web/nlp'
|
||||
export { registerAiAssistantRoutes } from './routes/web/ai-assistants'
|
||||
export { registerAiSkillRoutes } from './routes/web/ai-skills'
|
||||
export { registerAiLlmRoutes } from './routes/web/ai-llm'
|
||||
export { registerAiChatRoutes } from './routes/web/ai-chats'
|
||||
export { registerAiSummaryRoutes } from './routes/web/ai-summaries'
|
||||
export { registerAiLlmStreamRoutes } from './routes/web/ai-llm-stream'
|
||||
export { registerAiAgentStreamRoutes } from './routes/web/ai-agent-stream'
|
||||
export { registerAiToolRoutes } from './routes/web/ai-tools'
|
||||
export { registerTelemetryRoutes } from './routes/web/telemetry'
|
||||
export { registerAutomationRoutes } from './routes/web/automation'
|
||||
export { registerGlobalInsightRoutes } from './routes/web/global-insight'
|
||||
@@ -8,10 +8,10 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { HttpRouteContext } from './context'
|
||||
import { PreferencesManager } from '@openchatlab/node-runtime'
|
||||
import { registerSystemRoutes } from './routes/system'
|
||||
import { registerRestSessionRoutes } from './routes/sessions'
|
||||
import { createDatabaseRestSessionProvider } from './routes/rest-session-provider'
|
||||
import { registerImportRoutes } from './routes/imports'
|
||||
import { registerSystemRoutes } from './routes/rest/system'
|
||||
import { registerRestSessionRoutes } from './routes/rest/sessions'
|
||||
import { createDatabaseRestSessionProvider } from './routes/rest/session-provider'
|
||||
import { registerImportRoutes } from './routes/rest/imports'
|
||||
import { registerSessionRoutes } from './routes/web/sessions'
|
||||
import { registerMemberRoutes } from './routes/web/members'
|
||||
import { registerContactsRoutes } from './routes/web/contacts'
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export type {
|
||||
RestMessageQuery,
|
||||
RestMessagePage,
|
||||
RestSessionDetail,
|
||||
RestSessionExportData,
|
||||
RestSessionOverview,
|
||||
RestSessionProvider,
|
||||
RestSessionSummary,
|
||||
} from './routes/rest-session-provider'
|
||||
export { createDatabaseRestSessionProvider } from './routes/rest-session-provider'
|
||||
export { registerSystemRoutes } from './routes/system'
|
||||
export type { SystemRouteContext } from './routes/system'
|
||||
export { registerRestSessionRoutes } from './routes/sessions'
|
||||
+1
-1
@@ -6,7 +6,7 @@ import test from 'node:test'
|
||||
import Fastify from 'fastify'
|
||||
import type { PathProvider } from '@openchatlab/core'
|
||||
import { DatabaseManager } from '@openchatlab/node-runtime'
|
||||
import type { HttpRouteContext } from '../context'
|
||||
import type { HttpRouteContext } from '../../context'
|
||||
import { registerImportRoutes } from './imports'
|
||||
|
||||
const nativeBinding = path.resolve('apps/cli/native/better_sqlite3.node')
|
||||
+2
-2
@@ -7,10 +7,10 @@
|
||||
*/
|
||||
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { HttpRouteContext } from '../context'
|
||||
import type { HttpRouteContext } from '../../context'
|
||||
import { appLogger, pushImport } from '@openchatlab/node-runtime'
|
||||
import type { PushImportPayload } from '@openchatlab/node-runtime'
|
||||
import { createJsonPushImportHandler } from '../import/json-push-handler'
|
||||
import { createJsonPushImportHandler } from '../../import/json-push-handler'
|
||||
|
||||
export function registerImportRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {
|
||||
const handleJsonPushImport = createJsonPushImportHandler({
|
||||
@@ -0,0 +1,13 @@
|
||||
export type {
|
||||
RestMessageQuery,
|
||||
RestMessagePage,
|
||||
RestSessionDetail,
|
||||
RestSessionExportData,
|
||||
RestSessionOverview,
|
||||
RestSessionProvider,
|
||||
RestSessionSummary,
|
||||
} from './session-provider'
|
||||
export { createDatabaseRestSessionProvider } from './session-provider'
|
||||
export { registerSystemRoutes } from './system'
|
||||
export type { SystemRouteContext } from './system'
|
||||
export { registerRestSessionRoutes } from './sessions'
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import Fastify from 'fastify'
|
||||
import type { RestSessionProvider } from './rest-session-provider'
|
||||
import type { RestSessionProvider } from './session-provider'
|
||||
import { registerRestSessionRoutes } from './sessions'
|
||||
import { registerSystemRoutes } from './system'
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
/** Data access contract for the public REST session routes. */
|
||||
import {
|
||||
executeReadonlySql,
|
||||
getLastPlatformMessageId,
|
||||
+9
-2
@@ -6,8 +6,15 @@
|
||||
*/
|
||||
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { successResponse, errorResponse, sessionNotFound, exportTooLarge, sqlExecutionError, ApiError } from '../errors'
|
||||
import type { RestSessionProvider } from './rest-session-provider'
|
||||
import {
|
||||
successResponse,
|
||||
errorResponse,
|
||||
sessionNotFound,
|
||||
exportTooLarge,
|
||||
sqlExecutionError,
|
||||
ApiError,
|
||||
} from '../../errors'
|
||||
import type { RestSessionProvider } from './session-provider'
|
||||
|
||||
const EXPORT_MESSAGE_LIMIT = 100_000
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { successResponse } from '../errors'
|
||||
import { successResponse } from '../../errors'
|
||||
|
||||
export interface SystemRouteContext {
|
||||
getVersion: () => string
|
||||
@@ -16,8 +16,8 @@ import type { DatabaseAdapter, PathProvider, PreparedStatement, RunResult } from
|
||||
import { PreferencesManager, type DatabaseManager, type SessionRuntimeAdapter } from '@openchatlab/node-runtime'
|
||||
import type { HttpRouteContext } from '../context'
|
||||
import { registerSharedRoutes } from '../register'
|
||||
import { registerRestSessionRoutes } from './sessions'
|
||||
import { createDatabaseRestSessionProvider } from './rest-session-provider'
|
||||
import { registerRestSessionRoutes } from './rest/sessions'
|
||||
import { createDatabaseRestSessionProvider } from './rest/session-provider'
|
||||
import { registerAutomationRoutes } from './web/automation'
|
||||
import { registerSessionRoutes } from './web/sessions'
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* buildAnalyticsCacheKey tests.
|
||||
*
|
||||
* 运行:node --import tsx --test packages/http-routes/src/analytics-cache.test.ts
|
||||
* Run: pnpm test -- packages/http-routes/src/routes/web/analytics-cache.test.ts
|
||||
*
|
||||
* 键的确定性直接决定缓存命中率与正确性:等价请求必须同键,不同请求必须异键。
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { buildAnalyticsCacheKey, withAnalyticsCache } from './analytics-cache'
|
||||
import type { HttpRouteContext } from './context'
|
||||
import type { HttpRouteContext } from '../../context'
|
||||
|
||||
describe('buildAnalyticsCacheKey', () => {
|
||||
it('is independent of param key order', () => {
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as path from 'path'
|
||||
import { getDbFileVersion, getOrComputeAnalysisCache } from '@openchatlab/node-runtime'
|
||||
import type { HttpRouteContext } from './context'
|
||||
import type { HttpRouteContext } from '../../context'
|
||||
|
||||
/** Deterministic stringify: sorts object keys recursively and drops `undefined`. */
|
||||
function canonical(value: unknown): string {
|
||||
@@ -33,8 +33,8 @@ import {
|
||||
getRepeatAnalysis,
|
||||
} from '@openchatlab/core'
|
||||
import type { ClusterGraphOptions } from '@openchatlab/core'
|
||||
import { parseTimeFilter } from '../../helpers'
|
||||
import { withAnalyticsCache } from '../../analytics-cache'
|
||||
import { parseTimeFilter } from './time-filter'
|
||||
import { withAnalyticsCache } from './analytics-cache'
|
||||
|
||||
type FilteredQuery = { startTs?: string; endTs?: string; memberId?: string }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { HttpRouteContext } from '../../context'
|
||||
import { withAnalyticsCache } from '../../analytics-cache'
|
||||
import { withAnalyticsCache } from './analytics-cache'
|
||||
import type { WordFrequencyParams, SupportedLocale } from '@openchatlab/core'
|
||||
import { POS_TAG_DEFINITIONS } from '@openchatlab/core'
|
||||
import {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Shared helpers for route modules.
|
||||
* Time-filter query parsing for Web analytics routes.
|
||||
*/
|
||||
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
@@ -17,6 +17,7 @@
|
||||
"@earendil-works/pi-agent-core": "0.74.2",
|
||||
"@earendil-works/pi-ai": "0.74.2",
|
||||
"@huggingface/transformers": "^4.2.0",
|
||||
"@openchatlab/config": "workspace:*",
|
||||
"@openchatlab/core": "workspace:*",
|
||||
"better-sqlite3": "^12.4.6",
|
||||
"gray-matter": "^4.0.3",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import type { ConfigStorage } from '@openchatlab/node-runtime'
|
||||
import type { ConfigStorage } from './llm-config-store'
|
||||
import { createAuthProfileLlmConfigStore } from './auth-profile-llm-config-store'
|
||||
|
||||
function createMemoryStorage(): ConfigStorage & { data: Map<string, unknown> } {
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { deleteAuthProfile, resolveApiKey, writeAuthProfile, type AuthProfile } from '@openchatlab/config'
|
||||
import { LLMConfigStore, type AIServiceConfig, type ConfigStorage } from '@openchatlab/node-runtime'
|
||||
import { LLMConfigStore, type AIServiceConfig, type ConfigStorage } from './llm-config-store'
|
||||
|
||||
export interface AuthProfileLlmConfigStoreDeps {
|
||||
resolveApiKey: (provider: string, authProfile?: string) => string | undefined
|
||||
@@ -164,6 +164,8 @@ export type { SummaryDeps, SummaryMessage, SummaryOptions, SummaryResult, Summar
|
||||
export { LLMConfigStore, MAX_CONFIG_COUNT } from './llm-config-store'
|
||||
export type { AIServiceConfig, AIConfigStore, ConfigStorage, LLMConfigStoreDeps } from './llm-config-store'
|
||||
export { createFileConfigStorage } from './file-config-storage'
|
||||
export { createAuthProfileLlmConfigStore } from './auth-profile-llm-config-store'
|
||||
export type { AuthProfileLlmConfigStoreDeps } from './auth-profile-llm-config-store'
|
||||
|
||||
// Custom Provider/Model Store
|
||||
export { CustomProviderStore, CustomModelStore } from './custom-store'
|
||||
|
||||
@@ -157,7 +157,8 @@ export type { AssistantConfig, AssistantSummary, SkillDef, SkillSummary } from '
|
||||
export { parseAssistantFile, serializeAssistant, parseSkillFile, extractSkillId } from './ai'
|
||||
export { AIChatManager } from './ai'
|
||||
export { countTokens, countMessagesTokens, initTokenizer } from './ai'
|
||||
export { createFileConfigStorage } from './ai'
|
||||
export { createFileConfigStorage, createAuthProfileLlmConfigStore } from './ai'
|
||||
export type { AuthProfileLlmConfigStoreDeps } from './ai'
|
||||
|
||||
// Assistant Manager
|
||||
export { AssistantManager } from './ai'
|
||||
|
||||
Generated
+3
@@ -331,6 +331,9 @@ importers:
|
||||
'@huggingface/transformers':
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
'@openchatlab/config':
|
||||
specifier: workspace:*
|
||||
version: link:../config
|
||||
'@openchatlab/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
|
||||
Reference in new issue
Block a user