@unified-llm/core
Version:
Unified LLM interface (in-memory).
248 lines • 10.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.clientRepository = exports.ClientRepository = void 0;
const drizzle_orm_1 = require("drizzle-orm");
const connection_1 = require("./connection");
const schema_1 = require("./schema");
const crypto_1 = require("crypto");
class ClientRepository {
async getDb() {
return await (0, connection_1.getDatabase)();
}
async isDisabled() {
return (await this.getDb()) === null;
}
/* ────────────────────────────────
CRUD
──────────────────────────────── */
/** Save or update LLM client configuration. */
async save(config) {
var _a, _b, _c, _d, _e, _f, _g;
if (await this.isDisabled()) {
// 永続化オフ時はメモリ上で完結
const now = new Date();
return {
id: config.id || `asst_${(0, crypto_1.randomUUID)()}`,
createdAt: now,
updatedAt: now,
isActive: (_a = config.isActive) !== null && _a !== void 0 ? _a : true,
name: config.name,
provider: config.provider,
description: (_b = config.description) !== null && _b !== void 0 ? _b : null,
model: (_c = config.model) !== null && _c !== void 0 ? _c : null,
systemPrompt: (_d = config.systemPrompt) !== null && _d !== void 0 ? _d : null,
instructions: (_e = config.instructions) !== null && _e !== void 0 ? _e : null,
apiKey: (_f = config.apiKey) !== null && _f !== void 0 ? _f : null,
generationConfig: config.generationConfig
? JSON.stringify(config.generationConfig)
: null,
tools: config.tools ? JSON.stringify(config.tools) : null,
argumentMap: config.argumentMap
? JSON.stringify(config.argumentMap)
: null,
tags: config.tags ? JSON.stringify(config.tags) : null,
metadata: config.metadata ? JSON.stringify(config.metadata) : null,
};
}
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
const now = new Date();
const id = config.id || `asst_${(0, crypto_1.randomUUID)()}`;
const newClient = {
id,
name: config.name,
description: config.description,
provider: config.provider,
model: config.model,
systemPrompt: config.systemPrompt,
instructions: config.instructions,
apiKey: config.apiKey,
generationConfig: config.generationConfig
? JSON.stringify(config.generationConfig)
: null,
tools: config.tools ? JSON.stringify(config.tools) : null,
argumentMap: config.argumentMap
? JSON.stringify(config.argumentMap)
: null,
tags: config.tags ? JSON.stringify(config.tags) : null,
isActive: (_g = config.isActive) !== null && _g !== void 0 ? _g : true,
createdAt: now,
updatedAt: now,
metadata: config.metadata ? JSON.stringify(config.metadata) : null,
};
const existing = await this.findById(id);
if (existing) {
await db
.update(schema_1.llmClients)
.set({ ...newClient, createdAt: existing.createdAt })
.where((0, drizzle_orm_1.eq)(schema_1.llmClients.id, id))
.run();
}
else {
await db.insert(schema_1.llmClients).values(newClient).run();
}
return (await this.findById(id));
}
/** Retrieve by ID */
async findById(id) {
var _a;
if (await this.isDisabled())
return null;
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
const result = await db
.select()
.from(schema_1.llmClients)
.where((0, drizzle_orm_1.eq)(schema_1.llmClients.id, id))
.limit(1);
return (_a = result[0]) !== null && _a !== void 0 ? _a : null;
}
/** 名前検索 */
async findByName(name) {
if (await this.isDisabled())
return [];
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
return await db
.select()
.from(schema_1.llmClients)
.where((0, drizzle_orm_1.like)(schema_1.llmClients.name, `%${name}%`))
.orderBy((0, drizzle_orm_1.desc)(schema_1.llmClients.updatedAt));
}
/** プロバイダー検索 */
async findByProvider(provider) {
if (await this.isDisabled())
return [];
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
return await db
.select()
.from(schema_1.llmClients)
.where((0, drizzle_orm_1.eq)(schema_1.llmClients.provider, provider))
.orderBy((0, drizzle_orm_1.desc)(schema_1.llmClients.updatedAt));
}
/** タグ検索 */
async findByTags(tags) {
if (await this.isDisabled())
return [];
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
const all = await db.select().from(schema_1.llmClients);
return all
.filter((c) => {
if (!c.tags)
return false;
const arr = JSON.parse(c.tags);
return tags.some((t) => arr.includes(t));
})
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
}
/** 一覧取得(非アクティブ含むか) */
async findAll(includeInactive = false) {
if (await this.isDisabled())
return [];
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
const q = db.select().from(schema_1.llmClients);
if (!includeInactive)
q.where((0, drizzle_orm_1.eq)(schema_1.llmClients.isActive, true));
return await q.orderBy((0, drizzle_orm_1.desc)(schema_1.llmClients.updatedAt));
}
/** 論理削除 */
async delete(id) {
var _a;
if (await this.isDisabled())
return false;
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
const res = await db
.update(schema_1.llmClients)
.set({ isActive: false, updatedAt: new Date() })
.where((0, drizzle_orm_1.eq)(schema_1.llmClients.id, id))
.run();
const affected = typeof res.rowsAffected === 'bigint'
? Number(res.rowsAffected)
: (_a = res.rowsAffected) !== null && _a !== void 0 ? _a : 0;
return affected > 0;
}
/** 物理削除 */
async hardDelete(id) {
var _a;
if (await this.isDisabled())
return false;
const db = await this.getDb();
if (!db)
throw new Error('Database connection is not available');
// 参照削除 → messages の client_id を null → 本体削除
await db.delete(schema_1.threadParticipants).where((0, drizzle_orm_1.eq)(schema_1.threadParticipants.clientId, id)).run();
await db
.update(schema_1.messages)
.set({ clientId: null })
.where((0, drizzle_orm_1.eq)(schema_1.messages.clientId, id))
.run();
const res = await db.delete(schema_1.llmClients).where((0, drizzle_orm_1.eq)(schema_1.llmClients.id, id)).run();
const affected = typeof res.rowsAffected === 'bigint'
? Number(res.rowsAffected)
: (_a = res.rowsAffected) !== null && _a !== void 0 ? _a : 0;
return affected > 0;
}
/* ────────────────────────────────
Utility
──────────────────────────────── */
static toConfig(stored) {
var _a, _b, _c, _d, _e, _f;
return {
id: stored.id,
name: stored.name,
description: (_a = stored.description) !== null && _a !== void 0 ? _a : undefined,
provider: stored.provider,
model: (_b = stored.model) !== null && _b !== void 0 ? _b : undefined,
systemPrompt: (_c = stored.systemPrompt) !== null && _c !== void 0 ? _c : undefined,
instructions: (_d = stored.instructions) !== null && _d !== void 0 ? _d : undefined,
apiKey: (_e = stored.apiKey) !== null && _e !== void 0 ? _e : undefined,
generationConfig: stored.generationConfig
? JSON.parse(stored.generationConfig)
: undefined,
tools: stored.tools ? JSON.parse(stored.tools) : undefined,
argumentMap: stored.argumentMap
? JSON.parse(stored.argumentMap)
: undefined,
tags: stored.tags ? JSON.parse(stored.tags) : undefined,
isActive: (_f = stored.isActive) !== null && _f !== void 0 ? _f : undefined,
metadata: stored.metadata ? JSON.parse(stored.metadata) : undefined,
};
}
static validateConfig(config) {
var _a;
const errors = [];
if (!((_a = config.name) === null || _a === void 0 ? void 0 : _a.trim()))
errors.push('Name is required');
if (!config.provider) {
errors.push('Provider is required');
}
else if (!['openai', 'anthropic', 'google', 'deepseek'].includes(config.provider)) {
errors.push('Provider must be one of: openai, anthropic, google, deepseek');
}
const gen = config.generationConfig;
if (gen) {
if (gen.temperature !== undefined && (gen.temperature < 0 || gen.temperature > 2))
errors.push('Temperature must be between 0 and 2');
if (gen.max_tokens !== undefined && gen.max_tokens <= 0)
errors.push('Max tokens must be greater than 0');
if (gen.top_p !== undefined && (gen.top_p < 0 || gen.top_p > 1))
errors.push('Top P must be between 0 and 1');
}
return { valid: errors.length === 0, errors };
}
}
exports.ClientRepository = ClientRepository;
/* シングルトン */
exports.clientRepository = new ClientRepository();
//# sourceMappingURL=client-repository.js.map