use environment variables from cloudflare's environment
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
// export { db } from "./lowdb";
|
// export { db } from "./lowdb";
|
||||||
export { db } from "./postgres";
|
export { getDb, getDbClient } from "./postgres";
|
||||||
|
|||||||
+245
-236
@@ -10,7 +10,6 @@ import type {
|
|||||||
MessageEntity,
|
MessageEntity,
|
||||||
} from "./common.ts";
|
} from "./common.ts";
|
||||||
import type { CommittedMessage } from "../types";
|
import type { CommittedMessage } from "../types";
|
||||||
import { env } from "../server/env.js";
|
|
||||||
|
|
||||||
// export const pool = new Pool({
|
// export const pool = new Pool({
|
||||||
// connectionString: env.POSTGRES_CONNECTION_STRING as string,
|
// connectionString: env.POSTGRES_CONNECTION_STRING as string,
|
||||||
@@ -21,244 +20,254 @@ import { env } from "../server/env.js";
|
|||||||
// pool,
|
// pool,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const dialect = new NeonDialect({
|
export function getDbClient(POSTGRES_CONNECTION_STRING: string) {
|
||||||
neon: neon(env.POSTGRES_CONNECTION_STRING as string),
|
const dialect = new NeonDialect({
|
||||||
});
|
neon: neon(POSTGRES_CONNECTION_STRING as string),
|
||||||
|
});
|
||||||
|
|
||||||
// Database interface is passed to Kysely's constructor, and from now on, Kysely
|
// Database interface is passed to Kysely's constructor, and from now on, Kysely
|
||||||
// knows your database structure.
|
// knows your database structure.
|
||||||
// Dialect is passed to Kysely's constructor, and from now on, Kysely knows how
|
// Dialect is passed to Kysely's constructor, and from now on, Kysely knows how
|
||||||
// to communicate with your database.
|
// to communicate with your database.
|
||||||
export const dbClient = new Kysely<Database>({
|
const dbClient = new Kysely<Database>({
|
||||||
dialect,
|
dialect,
|
||||||
});
|
});
|
||||||
|
|
||||||
const conversations: ConversationEntity = {
|
return dbClient;
|
||||||
construct: (conversation) => conversation,
|
}
|
||||||
create: async (conversation) => {
|
|
||||||
const insertedRows = await dbClient
|
|
||||||
.insertInto("conversations")
|
|
||||||
.values(conversation)
|
|
||||||
.returningAll()
|
|
||||||
.execute();
|
|
||||||
return insertedRows[0];
|
|
||||||
},
|
|
||||||
createMany: async (conversations) => {
|
|
||||||
const insertedRows = await dbClient
|
|
||||||
.insertInto("conversations")
|
|
||||||
.values(conversations)
|
|
||||||
.returningAll()
|
|
||||||
.execute();
|
|
||||||
return insertedRows;
|
|
||||||
},
|
|
||||||
findAll: async () => {
|
|
||||||
const rows = await dbClient
|
|
||||||
.selectFrom("conversations")
|
|
||||||
.selectAll()
|
|
||||||
.execute();
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
findById: async (id) => {
|
|
||||||
const row = await dbClient
|
|
||||||
.selectFrom("conversations")
|
|
||||||
.selectAll()
|
|
||||||
.where("id", "=", id)
|
|
||||||
.execute();
|
|
||||||
return row[0];
|
|
||||||
},
|
|
||||||
update: async (id, data) => {
|
|
||||||
await dbClient
|
|
||||||
.updateTable("conversations")
|
|
||||||
.set(data)
|
|
||||||
.where("id", "=", id)
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
delete: async (id) => {
|
|
||||||
await dbClient.deleteFrom("conversations").where("id", "=", id).execute();
|
|
||||||
},
|
|
||||||
fetchMessages: async (conversationId) => {
|
|
||||||
const rows = await dbClient
|
|
||||||
.selectFrom("messages")
|
|
||||||
.selectAll()
|
|
||||||
.where("conversationId", "=", conversationId)
|
|
||||||
.execute();
|
|
||||||
return rows as Array<CommittedMessage>;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const facts: FactEntity = {
|
export function getDb(POSTGRES_CONNECTION_STRING: string) {
|
||||||
construct: (fact) => fact,
|
const dbClient = getDbClient(POSTGRES_CONNECTION_STRING);
|
||||||
create: async (fact) => {
|
|
||||||
const insertedRows = await dbClient
|
|
||||||
.insertInto("facts")
|
|
||||||
.values(fact)
|
|
||||||
.returningAll()
|
|
||||||
.execute();
|
|
||||||
return insertedRows[0];
|
|
||||||
},
|
|
||||||
createMany: async (facts) => {
|
|
||||||
const insertedRows = await dbClient
|
|
||||||
.insertInto("facts")
|
|
||||||
.values(facts)
|
|
||||||
.returningAll()
|
|
||||||
.execute();
|
|
||||||
return insertedRows;
|
|
||||||
},
|
|
||||||
findAll: async () => {
|
|
||||||
const rows = await dbClient.selectFrom("facts").selectAll().execute();
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
findById: async (id) => {
|
|
||||||
const row = await dbClient
|
|
||||||
.selectFrom("facts")
|
|
||||||
.selectAll()
|
|
||||||
.where("id", "=", id)
|
|
||||||
.execute();
|
|
||||||
return row[0];
|
|
||||||
},
|
|
||||||
update: async (id, data) => {
|
|
||||||
await dbClient
|
|
||||||
.updateTable("facts")
|
|
||||||
.set(data)
|
|
||||||
.where("id", "=", id)
|
|
||||||
.execute();
|
|
||||||
},
|
|
||||||
delete: async (id) => {
|
|
||||||
await dbClient.deleteFrom("facts").where("id", "=", id).execute();
|
|
||||||
},
|
|
||||||
findByConversationId: async (conversationId) => {
|
|
||||||
const rows = await dbClient
|
|
||||||
.selectFrom("facts")
|
|
||||||
.innerJoin("messages", "messages.id", "facts.sourceMessageId")
|
|
||||||
.selectAll("facts")
|
|
||||||
.where("conversationId", "=", conversationId)
|
|
||||||
.execute();
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const factTriggers: FactTriggerEntity = {
|
const conversations: ConversationEntity = {
|
||||||
construct: (factTrigger) => factTrigger,
|
construct: (conversation) => conversation,
|
||||||
create: async (factTrigger) => {
|
create: async (conversation) => {
|
||||||
const insertedRows = await dbClient
|
const insertedRows = await dbClient
|
||||||
.insertInto("fact_triggers")
|
.insertInto("conversations")
|
||||||
.values(factTrigger)
|
.values(conversation)
|
||||||
.returningAll()
|
.returningAll()
|
||||||
.execute();
|
.execute();
|
||||||
return insertedRows[0];
|
return insertedRows[0];
|
||||||
},
|
},
|
||||||
createMany: async (factTriggers) => {
|
createMany: async (conversations) => {
|
||||||
const insertedRows = await dbClient
|
const insertedRows = await dbClient
|
||||||
.insertInto("fact_triggers")
|
.insertInto("conversations")
|
||||||
.values(factTriggers)
|
.values(conversations)
|
||||||
.returningAll()
|
.returningAll()
|
||||||
.execute();
|
.execute();
|
||||||
return insertedRows;
|
return insertedRows;
|
||||||
},
|
},
|
||||||
findAll: async () => {
|
findAll: async () => {
|
||||||
const rows = await dbClient
|
const rows = await dbClient
|
||||||
.selectFrom("fact_triggers")
|
.selectFrom("conversations")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.execute();
|
.execute();
|
||||||
return rows;
|
return rows;
|
||||||
},
|
},
|
||||||
findById: async (id) => {
|
findById: async (id) => {
|
||||||
const row = await dbClient
|
const row = await dbClient
|
||||||
.selectFrom("fact_triggers")
|
.selectFrom("conversations")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where("id", "=", id)
|
.where("id", "=", id)
|
||||||
.execute();
|
.execute();
|
||||||
return row[0];
|
return row[0];
|
||||||
},
|
},
|
||||||
update: async (id, data) => {
|
update: async (id, data) => {
|
||||||
await dbClient
|
await dbClient
|
||||||
.updateTable("fact_triggers")
|
.updateTable("conversations")
|
||||||
.set(data)
|
.set(data)
|
||||||
.where("id", "=", id)
|
.where("id", "=", id)
|
||||||
.execute();
|
.execute();
|
||||||
},
|
},
|
||||||
delete: async (id) => {
|
delete: async (id) => {
|
||||||
await dbClient.deleteFrom("fact_triggers").where("id", "=", id).execute();
|
await dbClient.deleteFrom("conversations").where("id", "=", id).execute();
|
||||||
},
|
},
|
||||||
findByFactId: async (factId) => {
|
fetchMessages: async (conversationId) => {
|
||||||
const rows = await dbClient
|
const rows = await dbClient
|
||||||
.selectFrom("fact_triggers")
|
.selectFrom("messages")
|
||||||
.innerJoin("facts", "facts.id", "fact_triggers.sourceFactId")
|
.selectAll()
|
||||||
.selectAll("fact_triggers")
|
.where("conversationId", "=", conversationId)
|
||||||
.where("sourceFactId", "=", factId)
|
.execute();
|
||||||
.execute();
|
return rows as Array<CommittedMessage>;
|
||||||
return rows;
|
},
|
||||||
},
|
};
|
||||||
findByConversationId: async (conversationId) => {
|
|
||||||
const rows = await dbClient
|
|
||||||
.selectFrom("fact_triggers")
|
|
||||||
.innerJoin("facts", "facts.id", "fact_triggers.sourceFactId")
|
|
||||||
.innerJoin("messages", "messages.id", "facts.sourceMessageId")
|
|
||||||
.selectAll("fact_triggers")
|
|
||||||
.where("messages.conversationId", "=", conversationId)
|
|
||||||
.execute();
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const messages: MessageEntity = {
|
const facts: FactEntity = {
|
||||||
construct: (message) => message,
|
construct: (fact) => fact,
|
||||||
create: async (message) => {
|
create: async (fact) => {
|
||||||
const insertedRows = await dbClient
|
const insertedRows = await dbClient
|
||||||
.insertInto("messages")
|
.insertInto("facts")
|
||||||
.values({ ...message, parts: JSON.stringify(message.parts) })
|
.values(fact)
|
||||||
.returningAll()
|
.returningAll()
|
||||||
.execute();
|
.execute();
|
||||||
return insertedRows[0] as CommittedMessage;
|
return insertedRows[0];
|
||||||
},
|
},
|
||||||
createMany: async (messages) => {
|
createMany: async (facts) => {
|
||||||
const insertedRows = await dbClient
|
const insertedRows = await dbClient
|
||||||
.insertInto("messages")
|
.insertInto("facts")
|
||||||
.values(
|
.values(facts)
|
||||||
messages.map((message) => ({
|
.returningAll()
|
||||||
...message,
|
.execute();
|
||||||
parts: JSON.stringify(message.parts),
|
return insertedRows;
|
||||||
}))
|
},
|
||||||
)
|
findAll: async () => {
|
||||||
.returningAll()
|
const rows = await dbClient.selectFrom("facts").selectAll().execute();
|
||||||
.execute();
|
return rows;
|
||||||
return insertedRows as Array<CommittedMessage>;
|
},
|
||||||
},
|
findById: async (id) => {
|
||||||
findAll: async () => {
|
const row = await dbClient
|
||||||
const rows = await dbClient.selectFrom("messages").selectAll().execute();
|
.selectFrom("facts")
|
||||||
return rows as Array<CommittedMessage>;
|
.selectAll()
|
||||||
},
|
.where("id", "=", id)
|
||||||
findById: async (id) => {
|
.execute();
|
||||||
const row = await dbClient
|
return row[0];
|
||||||
.selectFrom("messages")
|
},
|
||||||
.selectAll()
|
update: async (id, data) => {
|
||||||
.where("id", "=", id)
|
await dbClient
|
||||||
.execute();
|
.updateTable("facts")
|
||||||
return row[0] as CommittedMessage;
|
.set(data)
|
||||||
},
|
.where("id", "=", id)
|
||||||
update: async (id, data) => {
|
.execute();
|
||||||
await dbClient
|
},
|
||||||
.updateTable("messages")
|
delete: async (id) => {
|
||||||
.set(data)
|
await dbClient.deleteFrom("facts").where("id", "=", id).execute();
|
||||||
.where("id", "=", id)
|
},
|
||||||
.execute();
|
findByConversationId: async (conversationId) => {
|
||||||
},
|
const rows = await dbClient
|
||||||
delete: async (id) => {
|
.selectFrom("facts")
|
||||||
await dbClient.deleteFrom("messages").where("id", "=", id).execute();
|
.innerJoin("messages", "messages.id", "facts.sourceMessageId")
|
||||||
},
|
.selectAll("facts")
|
||||||
findByConversationId: async (conversationId) => {
|
.where("conversationId", "=", conversationId)
|
||||||
const rows = (await dbClient
|
.execute();
|
||||||
.selectFrom("messages")
|
return rows;
|
||||||
.selectAll()
|
},
|
||||||
.where("conversationId", "=", conversationId)
|
};
|
||||||
.execute()) as Array<CommittedMessage>;
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const db = {
|
const factTriggers: FactTriggerEntity = {
|
||||||
conversations,
|
construct: (factTrigger) => factTrigger,
|
||||||
facts,
|
create: async (factTrigger) => {
|
||||||
factTriggers,
|
const insertedRows = await dbClient
|
||||||
messages,
|
.insertInto("fact_triggers")
|
||||||
};
|
.values(factTrigger)
|
||||||
|
.returningAll()
|
||||||
|
.execute();
|
||||||
|
return insertedRows[0];
|
||||||
|
},
|
||||||
|
createMany: async (factTriggers) => {
|
||||||
|
const insertedRows = await dbClient
|
||||||
|
.insertInto("fact_triggers")
|
||||||
|
.values(factTriggers)
|
||||||
|
.returningAll()
|
||||||
|
.execute();
|
||||||
|
return insertedRows;
|
||||||
|
},
|
||||||
|
findAll: async () => {
|
||||||
|
const rows = await dbClient
|
||||||
|
.selectFrom("fact_triggers")
|
||||||
|
.selectAll()
|
||||||
|
.execute();
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
findById: async (id) => {
|
||||||
|
const row = await dbClient
|
||||||
|
.selectFrom("fact_triggers")
|
||||||
|
.selectAll()
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
return row[0];
|
||||||
|
},
|
||||||
|
update: async (id, data) => {
|
||||||
|
await dbClient
|
||||||
|
.updateTable("fact_triggers")
|
||||||
|
.set(data)
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
delete: async (id) => {
|
||||||
|
await dbClient.deleteFrom("fact_triggers").where("id", "=", id).execute();
|
||||||
|
},
|
||||||
|
findByFactId: async (factId) => {
|
||||||
|
const rows = await dbClient
|
||||||
|
.selectFrom("fact_triggers")
|
||||||
|
.innerJoin("facts", "facts.id", "fact_triggers.sourceFactId")
|
||||||
|
.selectAll("fact_triggers")
|
||||||
|
.where("sourceFactId", "=", factId)
|
||||||
|
.execute();
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
findByConversationId: async (conversationId) => {
|
||||||
|
const rows = await dbClient
|
||||||
|
.selectFrom("fact_triggers")
|
||||||
|
.innerJoin("facts", "facts.id", "fact_triggers.sourceFactId")
|
||||||
|
.innerJoin("messages", "messages.id", "facts.sourceMessageId")
|
||||||
|
.selectAll("fact_triggers")
|
||||||
|
.where("messages.conversationId", "=", conversationId)
|
||||||
|
.execute();
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const messages: MessageEntity = {
|
||||||
|
construct: (message) => message,
|
||||||
|
create: async (message) => {
|
||||||
|
const insertedRows = await dbClient
|
||||||
|
.insertInto("messages")
|
||||||
|
.values({ ...message, parts: JSON.stringify(message.parts) })
|
||||||
|
.returningAll()
|
||||||
|
.execute();
|
||||||
|
return insertedRows[0] as CommittedMessage;
|
||||||
|
},
|
||||||
|
createMany: async (messages) => {
|
||||||
|
const insertedRows = await dbClient
|
||||||
|
.insertInto("messages")
|
||||||
|
.values(
|
||||||
|
messages.map((message) => ({
|
||||||
|
...message,
|
||||||
|
parts: JSON.stringify(message.parts),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.returningAll()
|
||||||
|
.execute();
|
||||||
|
return insertedRows as Array<CommittedMessage>;
|
||||||
|
},
|
||||||
|
findAll: async () => {
|
||||||
|
const rows = await dbClient.selectFrom("messages").selectAll().execute();
|
||||||
|
return rows as Array<CommittedMessage>;
|
||||||
|
},
|
||||||
|
findById: async (id) => {
|
||||||
|
const row = await dbClient
|
||||||
|
.selectFrom("messages")
|
||||||
|
.selectAll()
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
return row[0] as CommittedMessage;
|
||||||
|
},
|
||||||
|
update: async (id, data) => {
|
||||||
|
await dbClient
|
||||||
|
.updateTable("messages")
|
||||||
|
.set(data)
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
},
|
||||||
|
delete: async (id) => {
|
||||||
|
await dbClient.deleteFrom("messages").where("id", "=", id).execute();
|
||||||
|
},
|
||||||
|
findByConversationId: async (conversationId) => {
|
||||||
|
const rows = (await dbClient
|
||||||
|
.selectFrom("messages")
|
||||||
|
.selectAll()
|
||||||
|
.where("conversationId", "=", conversationId)
|
||||||
|
.execute()) as Array<CommittedMessage>;
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const db = {
|
||||||
|
conversations,
|
||||||
|
facts,
|
||||||
|
factTriggers,
|
||||||
|
messages,
|
||||||
|
};
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|||||||
+12
-1
@@ -1,11 +1,22 @@
|
|||||||
import type { PageContextServer } from "vike/types";
|
import type { PageContextServer } from "vike/types";
|
||||||
import { createCaller } from "../trpc.js";
|
import { createCaller } from "../trpc.js";
|
||||||
|
import { getDb } from "../../../database/postgres.js";
|
||||||
|
import { getOpenrouter } from "../provider.js";
|
||||||
|
import { env } from "../../../server/env.js";
|
||||||
|
|
||||||
export type Data = Awaited<ReturnType<typeof data>>;
|
export type Data = Awaited<ReturnType<typeof data>>;
|
||||||
|
|
||||||
export const data = async (pageContext: PageContextServer) => {
|
export const data = async (pageContext: PageContextServer) => {
|
||||||
const { id } = pageContext.routeParams;
|
const { id } = pageContext.routeParams;
|
||||||
const caller = createCaller({});
|
const caller = createCaller({
|
||||||
|
db: getDb(
|
||||||
|
(pageContext.env?.POSTGRES_CONNECTION_STRING ||
|
||||||
|
env.POSTGRES_CONNECTION_STRING) as string
|
||||||
|
),
|
||||||
|
openrouter: getOpenrouter(
|
||||||
|
(pageContext.env?.OPENROUTER_API_KEY || env.OPENROUTER_API_KEY) as string
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
const [
|
const [
|
||||||
conversation,
|
conversation,
|
||||||
|
|||||||
@@ -3,18 +3,17 @@ import {
|
|||||||
publicProcedure,
|
publicProcedure,
|
||||||
createCallerFactory,
|
createCallerFactory,
|
||||||
} from "../../trpc/server";
|
} from "../../trpc/server";
|
||||||
import { db } from "../../database/index";
|
|
||||||
|
|
||||||
export const conversations = router({
|
export const conversations = router({
|
||||||
fetchAll: publicProcedure.query(async () => {
|
fetchAll: publicProcedure.query(async ({ ctx: { db } }) => {
|
||||||
return await db.conversations.findAll();
|
return await db.conversations.findAll();
|
||||||
}),
|
}),
|
||||||
fetchOne: publicProcedure
|
fetchOne: publicProcedure
|
||||||
.input((x) => x as { id: string })
|
.input((x) => x as { id: string })
|
||||||
.query(async ({ input: { id } }) => {
|
.query(async ({ input: { id }, ctx: { db } }) => {
|
||||||
return await db.conversations.findById(id);
|
return await db.conversations.findById(id);
|
||||||
}),
|
}),
|
||||||
start: publicProcedure.mutation(async () => {
|
start: publicProcedure.mutation(async ({ ctx: { db } }) => {
|
||||||
const row = {
|
const row = {
|
||||||
title: "New Conversation",
|
title: "New Conversation",
|
||||||
userId: "019900bb-61b3-7333-b760-b27784dfe33b",
|
userId: "019900bb-61b3-7333-b760-b27784dfe33b",
|
||||||
@@ -23,7 +22,7 @@ export const conversations = router({
|
|||||||
}),
|
}),
|
||||||
deleteOne: publicProcedure
|
deleteOne: publicProcedure
|
||||||
.input((x) => x as { id: string })
|
.input((x) => x as { id: string })
|
||||||
.mutation(async ({ input: { id } }) => {
|
.mutation(async ({ input: { id }, ctx: { db } }) => {
|
||||||
await db.conversations.delete(id);
|
await db.conversations.delete(id);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
@@ -35,13 +34,13 @@ export const conversations = router({
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { id, title } }) => {
|
.mutation(async ({ input: { id, title }, ctx: { db } }) => {
|
||||||
await db.conversations.update(id, { title });
|
await db.conversations.update(id, { title });
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
fetchMessages: publicProcedure
|
fetchMessages: publicProcedure
|
||||||
.input((x) => x as { conversationId: string })
|
.input((x) => x as { conversationId: string })
|
||||||
.query(async ({ input: { conversationId } }) => {
|
.query(async ({ input: { conversationId }, ctx: { db } }) => {
|
||||||
return await db.conversations.fetchMessages(conversationId);
|
return await db.conversations.fetchMessages(conversationId);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import {
|
|||||||
publicProcedure,
|
publicProcedure,
|
||||||
createCallerFactory,
|
createCallerFactory,
|
||||||
} from "../../trpc/server.js";
|
} from "../../trpc/server.js";
|
||||||
import { db } from "../../database/index.js";
|
|
||||||
import type { DraftMessage } from "../../types.js";
|
import type { DraftMessage } from "../../types.js";
|
||||||
import { openrouter, MODEL_NAME } from "./provider.js";
|
import { MODEL_NAME } from "./provider.js";
|
||||||
import { generateObject, generateText, jsonSchema } from "ai";
|
import { generateObject, generateText, jsonSchema } from "ai";
|
||||||
import type { Fact } from "../../database/common.js";
|
import type { Fact } from "../../database/common.js";
|
||||||
|
|
||||||
@@ -61,12 +60,12 @@ Generate a list of situations in which the fact is useful.`;
|
|||||||
export const factTriggers = router({
|
export const factTriggers = router({
|
||||||
fetchByFactId: publicProcedure
|
fetchByFactId: publicProcedure
|
||||||
.input((x) => x as { factId: string })
|
.input((x) => x as { factId: string })
|
||||||
.query(async ({ input: { factId } }) => {
|
.query(async ({ input: { factId }, ctx: { db } }) => {
|
||||||
return db.factTriggers.findByFactId(factId);
|
return db.factTriggers.findByFactId(factId);
|
||||||
}),
|
}),
|
||||||
fetchByConversationId: publicProcedure
|
fetchByConversationId: publicProcedure
|
||||||
.input((x) => x as { conversationId: string })
|
.input((x) => x as { conversationId: string })
|
||||||
.query(async ({ input: { conversationId } }) => {
|
.query(async ({ input: { conversationId }, ctx: { db } }) => {
|
||||||
return await db.factTriggers.findByConversationId(conversationId);
|
return await db.factTriggers.findByConversationId(conversationId);
|
||||||
}),
|
}),
|
||||||
deleteOne: publicProcedure
|
deleteOne: publicProcedure
|
||||||
@@ -76,7 +75,7 @@ export const factTriggers = router({
|
|||||||
factTriggerId: string;
|
factTriggerId: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { factTriggerId } }) => {
|
.mutation(async ({ input: { factTriggerId }, ctx: { db } }) => {
|
||||||
await db.factTriggers.delete(factTriggerId);
|
await db.factTriggers.delete(factTriggerId);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
@@ -88,7 +87,7 @@ export const factTriggers = router({
|
|||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { factTriggerId, content } }) => {
|
.mutation(async ({ input: { factTriggerId, content }, ctx: { db } }) => {
|
||||||
db.factTriggers.update(factTriggerId, { content });
|
db.factTriggers.update(factTriggerId, { content });
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
@@ -110,6 +109,7 @@ export const factTriggers = router({
|
|||||||
mainResponseContent,
|
mainResponseContent,
|
||||||
fact,
|
fact,
|
||||||
},
|
},
|
||||||
|
ctx: { openrouter },
|
||||||
}) => {
|
}) => {
|
||||||
const factTriggers = await generateObject({
|
const factTriggers = await generateObject({
|
||||||
model: openrouter(MODEL_NAME),
|
model: openrouter(MODEL_NAME),
|
||||||
|
|||||||
+4
-4
@@ -3,7 +3,6 @@ import {
|
|||||||
publicProcedure,
|
publicProcedure,
|
||||||
createCallerFactory,
|
createCallerFactory,
|
||||||
} from "../../trpc/server.js";
|
} from "../../trpc/server.js";
|
||||||
import { db } from "../../database/index.js";
|
|
||||||
import type { DraftMessage } from "../../types.js";
|
import type { DraftMessage } from "../../types.js";
|
||||||
import { MODEL_NAME, openrouter } from "./provider.js";
|
import { MODEL_NAME, openrouter } from "./provider.js";
|
||||||
import { generateObject, generateText, jsonSchema } from "ai";
|
import { generateObject, generateText, jsonSchema } from "ai";
|
||||||
@@ -56,7 +55,7 @@ Extract new facts from these messages.`;
|
|||||||
export const facts = router({
|
export const facts = router({
|
||||||
fetchByConversationId: publicProcedure
|
fetchByConversationId: publicProcedure
|
||||||
.input((x) => x as { conversationId: string })
|
.input((x) => x as { conversationId: string })
|
||||||
.query(async ({ input: { conversationId } }) => {
|
.query(async ({ input: { conversationId }, ctx: { db } }) => {
|
||||||
return await db.facts.findByConversationId(conversationId);
|
return await db.facts.findByConversationId(conversationId);
|
||||||
}),
|
}),
|
||||||
deleteOne: publicProcedure
|
deleteOne: publicProcedure
|
||||||
@@ -66,7 +65,7 @@ export const facts = router({
|
|||||||
factId: string;
|
factId: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { factId } }) => {
|
.mutation(async ({ input: { factId }, ctx: { db } }) => {
|
||||||
await db.facts.delete(factId);
|
await db.facts.delete(factId);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
@@ -78,7 +77,7 @@ export const facts = router({
|
|||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { factId, content } }) => {
|
.mutation(async ({ input: { factId, content }, ctx: { db } }) => {
|
||||||
await db.facts.update(factId, { content });
|
await db.facts.update(factId, { content });
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}),
|
}),
|
||||||
@@ -100,6 +99,7 @@ export const facts = router({
|
|||||||
messagesSincePreviousRunningSummary,
|
messagesSincePreviousRunningSummary,
|
||||||
newMessages,
|
newMessages,
|
||||||
},
|
},
|
||||||
|
ctx: { db },
|
||||||
}) => {
|
}) => {
|
||||||
const factsFromUserMessageResponse = await generateObject({
|
const factsFromUserMessageResponse = await generateObject({
|
||||||
model: openrouter(MODEL_NAME),
|
model: openrouter(MODEL_NAME),
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import {
|
|||||||
publicProcedure,
|
publicProcedure,
|
||||||
createCallerFactory,
|
createCallerFactory,
|
||||||
} from "../../trpc/server";
|
} from "../../trpc/server";
|
||||||
import { MODEL_NAME, openrouter } from "./provider.js";
|
import { MODEL_NAME } from "./provider.js";
|
||||||
import { generateObject, generateText, jsonSchema } from "ai";
|
import { generateObject, generateText, jsonSchema } from "ai";
|
||||||
import type { DraftMessage } from "../../types.js";
|
import type { DraftMessage } from "../../types.js";
|
||||||
import { db } from "../../database/index";
|
|
||||||
|
|
||||||
const runningSummarySystemPrompt = ({
|
const runningSummarySystemPrompt = ({
|
||||||
previousRunningSummary,
|
previousRunningSummary,
|
||||||
@@ -51,12 +50,12 @@ Generate a new running summary of the conversation.`;
|
|||||||
export const messages = router({
|
export const messages = router({
|
||||||
fetchByConversationId: publicProcedure
|
fetchByConversationId: publicProcedure
|
||||||
.input((x) => x as { conversationId: string })
|
.input((x) => x as { conversationId: string })
|
||||||
.query(async ({ input: { conversationId } }) => {
|
.query(async ({ input: { conversationId }, ctx: { db } }) => {
|
||||||
return await db.messages.findByConversationId(conversationId);
|
return await db.messages.findByConversationId(conversationId);
|
||||||
}),
|
}),
|
||||||
deleteOne: publicProcedure
|
deleteOne: publicProcedure
|
||||||
.input((x) => x as { id: string })
|
.input((x) => x as { id: string })
|
||||||
.mutation(async ({ input: { id } }) => {
|
.mutation(async ({ input: { id }, ctx: { db } }) => {
|
||||||
await db.messages.delete(id);
|
await db.messages.delete(id);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}),
|
}),
|
||||||
@@ -76,6 +75,7 @@ export const messages = router({
|
|||||||
messagesSincePreviousRunningSummary,
|
messagesSincePreviousRunningSummary,
|
||||||
mainResponseContent,
|
mainResponseContent,
|
||||||
},
|
},
|
||||||
|
ctx: { openrouter },
|
||||||
}) => {
|
}) => {
|
||||||
const runningSummaryResponse = await generateText({
|
const runningSummaryResponse = await generateText({
|
||||||
model: openrouter(MODEL_NAME),
|
model: openrouter(MODEL_NAME),
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ export const openrouter = createOpenRouter({
|
|||||||
apiKey: env.OPENROUTER_API_KEY,
|
apiKey: env.OPENROUTER_API_KEY,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function getOpenrouter(OPENROUTER_API_KEY: string) {
|
||||||
|
return createOpenRouter({
|
||||||
|
apiKey: OPENROUTER_API_KEY,
|
||||||
|
});
|
||||||
|
}
|
||||||
export const MODEL_NAME = "mistralai/mistral-nemo";
|
export const MODEL_NAME = "mistralai/mistral-nemo";
|
||||||
// export const MODEL_NAME = "z-ai/glm-4.5-air";
|
// export const MODEL_NAME = "z-ai/glm-4.5-air";
|
||||||
// export const MODEL_NAME = "openai/gpt-5-mini";
|
// export const MODEL_NAME = "openai/gpt-5-mini";
|
||||||
|
|||||||
+7
-7
@@ -14,20 +14,15 @@ import type {
|
|||||||
// ConsistencyLevelEnum,
|
// ConsistencyLevelEnum,
|
||||||
// type NumberArrayId,
|
// type NumberArrayId,
|
||||||
// } from "@zilliz/milvus2-sdk-node";
|
// } from "@zilliz/milvus2-sdk-node";
|
||||||
import { db } from "../../database/index.js";
|
|
||||||
import { conversations } from "./conversations.js";
|
import { conversations } from "./conversations.js";
|
||||||
import { messages } from "./messages.js";
|
import { messages } from "./messages.js";
|
||||||
import { facts, createCaller as createCallerFacts } from "./facts.js";
|
import { facts, createCaller as createCallerFacts } from "./facts.js";
|
||||||
import { createCaller as createCallerMessages } from "./messages.js";
|
import { createCaller as createCallerMessages } from "./messages.js";
|
||||||
import { createCaller as createCallerFactTriggers } from "./fact-triggers.js";
|
import { createCaller as createCallerFactTriggers } from "./fact-triggers.js";
|
||||||
import { factTriggers } from "./fact-triggers.js";
|
import { factTriggers } from "./fact-triggers.js";
|
||||||
import { MODEL_NAME, openrouter } from "./provider.js";
|
import { MODEL_NAME } from "./provider.js";
|
||||||
import type { Fact, FactTrigger } from "../../database/common.js";
|
import type { Fact, FactTrigger } from "../../database/common.js";
|
||||||
|
|
||||||
const factsCaller = createCallerFacts({});
|
|
||||||
const messagesCaller = createCallerMessages({});
|
|
||||||
const factTriggerCaller = createCallerFactTriggers({});
|
|
||||||
|
|
||||||
const mainSystemPrompt = ({
|
const mainSystemPrompt = ({
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
previousRunningSummary,
|
previousRunningSummary,
|
||||||
@@ -54,7 +49,7 @@ export const chat = router({
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.subscription(async function* ({ input, signal }) {
|
.subscription(async function* ({ input, signal, ctx: { openrouter } }) {
|
||||||
const result = streamText({
|
const result = streamText({
|
||||||
model: openrouter(MODEL_NAME),
|
model: openrouter(MODEL_NAME),
|
||||||
messages: [{ role: "user" as const, content: input.message }],
|
messages: [{ role: "user" as const, content: input.message }],
|
||||||
@@ -76,7 +71,12 @@ export const chat = router({
|
|||||||
)
|
)
|
||||||
.subscription(async function* ({
|
.subscription(async function* ({
|
||||||
input: { conversationId, messages, systemPrompt, parameters },
|
input: { conversationId, messages, systemPrompt, parameters },
|
||||||
|
ctx,
|
||||||
}) {
|
}) {
|
||||||
|
const { db, openrouter } = ctx;
|
||||||
|
const factsCaller = createCallerFacts(ctx);
|
||||||
|
const messagesCaller = createCallerMessages(ctx);
|
||||||
|
const factTriggerCaller = createCallerFactTriggers(ctx);
|
||||||
/** TODO: Save all unsaved messages (i.e. those without an `id`) to the
|
/** TODO: Save all unsaved messages (i.e. those without an `id`) to the
|
||||||
* database. Is this dangerous? Can an attacker just send a bunch of
|
* database. Is this dangerous? Can an attacker just send a bunch of
|
||||||
* messages, omitting the ids, causing me to save a bunch of them to the
|
* messages, omitting the ids, causing me to save a bunch of them to the
|
||||||
|
|||||||
+23
-1
@@ -1,7 +1,14 @@
|
|||||||
import { appRouter } from "../trpc/router";
|
import { appRouter } from "../trpc/router";
|
||||||
// TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
|
// TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
|
||||||
import type { Get, UniversalHandler } from "@universal-middleware/core";
|
import {
|
||||||
|
type Get,
|
||||||
|
type UniversalHandler,
|
||||||
|
env as getEnv,
|
||||||
|
} from "@universal-middleware/core";
|
||||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||||
|
import { getDb, getDbClient } from "../database/postgres";
|
||||||
|
import { getOpenrouter } from "../pages/chat/provider";
|
||||||
|
import { env as processEnv } from "./env.js";
|
||||||
|
|
||||||
export const trpcHandler = ((endpoint) => (request, context, runtime) => {
|
export const trpcHandler = ((endpoint) => (request, context, runtime) => {
|
||||||
return fetchRequestHandler({
|
return fetchRequestHandler({
|
||||||
@@ -9,11 +16,26 @@ export const trpcHandler = ((endpoint) => (request, context, runtime) => {
|
|||||||
req: request,
|
req: request,
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
createContext({ req, resHeaders }) {
|
createContext({ req, resHeaders }) {
|
||||||
|
const env = getEnv(runtime);
|
||||||
|
const dbClient = getDbClient(
|
||||||
|
(env.POSTGRES_CONNECTION_STRING ||
|
||||||
|
processEnv.POSTGRES_CONNECTION_STRING) as string
|
||||||
|
);
|
||||||
|
const db = getDb(
|
||||||
|
(env.POSTGRES_CONNECTION_STRING ||
|
||||||
|
processEnv.POSTGRES_CONNECTION_STRING) as string
|
||||||
|
);
|
||||||
|
const openrouter = getOpenrouter(
|
||||||
|
(env.OPENROUTER_API_KEY || processEnv.OPENROUTER_API_KEY) as string
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...context,
|
...context,
|
||||||
...runtime,
|
...runtime,
|
||||||
req,
|
req,
|
||||||
resHeaders,
|
resHeaders,
|
||||||
|
dbClient,
|
||||||
|
db,
|
||||||
|
openrouter,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
allowMethodOverride: true,
|
allowMethodOverride: true,
|
||||||
|
|||||||
+10
-1
@@ -1,12 +1,21 @@
|
|||||||
import type { TSchema } from "@sinclair/typebox";
|
import type { TSchema } from "@sinclair/typebox";
|
||||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||||
import { initTRPC, TRPCError } from "@trpc/server";
|
import { initTRPC, TRPCError } from "@trpc/server";
|
||||||
|
import type { getDb } from "../database/postgres";
|
||||||
|
import type { getOpenrouter } from "../pages/chat/provider";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialization of tRPC backend
|
* Initialization of tRPC backend
|
||||||
* Should be done only once per backend!
|
* Should be done only once per backend!
|
||||||
*/
|
*/
|
||||||
const t = initTRPC.context<object>().create(/*{
|
const t = initTRPC
|
||||||
|
.context<
|
||||||
|
object & {
|
||||||
|
db: ReturnType<typeof getDb>;
|
||||||
|
openrouter: ReturnType<typeof getOpenrouter>;
|
||||||
|
}
|
||||||
|
>()
|
||||||
|
.create(/*{
|
||||||
sse: {
|
sse: {
|
||||||
maxDurationMs: 5 * 60 * 1_000, // 5 minutes
|
maxDurationMs: 5 * 60 * 1_000, // 5 minutes
|
||||||
ping: {
|
ping: {
|
||||||
|
|||||||
Reference in New Issue
Block a user