@undefineds.co/xpod 0.2.0-preview.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -2
- package/dist/agents/AgentExecutorFactory.js +1 -1
- package/dist/agents/AgentExecutorFactory.js.map +1 -1
- package/dist/agents/AgentManager.js +1 -1
- package/dist/agents/AgentManager.js.map +1 -1
- package/dist/agents/config/agent-meta-schema.d.ts +7 -7
- package/dist/agents/config/agent-meta-schema.js +1 -1
- package/dist/agents/config/agent-meta-schema.js.map +1 -1
- package/dist/agents/config/resolve.js +1 -1
- package/dist/agents/config/resolve.js.map +1 -1
- package/dist/agents/schema/agent-config.d.ts +18 -18
- package/dist/agents/schema/agent-config.js +1 -1
- package/dist/agents/schema/agent-config.js.map +1 -1
- package/dist/agents/schema/tables.d.ts +8 -8
- package/dist/agents/schema/tables.js +1 -1
- package/dist/agents/schema/tables.js.map +1 -1
- package/dist/ai/schema/config.d.ts +7 -7
- package/dist/ai/schema/config.js +1 -1
- package/dist/ai/schema/config.js.map +1 -1
- package/dist/ai/schema/model.d.ts +13 -13
- package/dist/ai/schema/model.js +1 -1
- package/dist/ai/schema/model.js.map +1 -1
- package/dist/ai/schema/provider.d.ts +7 -7
- package/dist/ai/schema/provider.js +1 -1
- package/dist/ai/schema/provider.js.map +1 -1
- package/dist/ai/schema/vector-store.d.ts +17 -17
- package/dist/ai/schema/vector-store.js +1 -1
- package/dist/ai/schema/vector-store.js.map +1 -1
- package/dist/ai/service/CredentialReaderImpl.js +1 -1
- package/dist/ai/service/CredentialReaderImpl.js.map +1 -1
- package/dist/ai/service/DefaultAiConfigService.js.map +1 -1
- package/dist/api/ApiServer.d.ts +3 -1
- package/dist/api/ApiServer.js +14 -1
- package/dist/api/ApiServer.js.map +1 -1
- package/dist/api/chatkit/pod-store.d.ts +6 -0
- package/dist/api/chatkit/pod-store.js +86 -35
- package/dist/api/chatkit/pod-store.js.map +1 -1
- package/dist/api/chatkit/schema.d.ts +29 -29
- package/dist/api/chatkit/schema.js +4 -4
- package/dist/api/chatkit/schema.js.map +1 -1
- package/dist/api/container/common.js +1 -0
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/runtime.js +21 -0
- package/dist/api/runtime.js.map +1 -1
- package/dist/api/service/VectorStoreService.js +1 -1
- package/dist/api/service/VectorStoreService.js.map +1 -1
- package/dist/cli/lib/pod-thread-store.js +1 -1
- package/dist/cli/lib/pod-thread-store.js.map +1 -1
- package/dist/credential/schema/tables.d.ts +14 -14
- package/dist/credential/schema/tables.js +1 -1
- package/dist/credential/schema/tables.js.map +1 -1
- package/dist/identity/drizzle/ServiceTokenRepository.js +9 -10
- package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
- package/dist/identity/drizzle/db.js +27 -2
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.js +16 -16
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/task/DrizzleTaskQueue.d.ts +1 -1
- package/dist/task/DrizzleTaskQueue.js +1 -1
- package/dist/task/DrizzleTaskQueue.js.map +1 -1
- package/dist/task/schema.d.ts +10 -10
- package/dist/task/schema.js +1 -1
- package/dist/task/schema.js.map +1 -1
- package/package.json +20 -10
package/dist/ai/schema/model.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.ProviderRelations = exports.ModelRelations = exports.Model = void 0;
|
|
19
|
-
const drizzle_solid_1 = require("drizzle-solid");
|
|
19
|
+
const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
|
|
20
20
|
const vocab_1 = require("../../vocab");
|
|
21
21
|
const provider_1 = require("./provider");
|
|
22
22
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/ai/schema/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/ai/schema/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH,gEAA+F;AAC/F,uCAAmD;AACnD,yCAAsC;AAEtC;;GAEG;AACU,QAAA,KAAK,GAAG,IAAA,wBAAQ,EAC3B,OAAO,EACP;IACE,EAAE,EAAE,IAAA,sBAAM,EAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC7B,WAAW,EAAE,IAAA,sBAAM,EAAC,aAAa,CAAC;IAClC,SAAS,EAAE,IAAA,sBAAM,EAAC,WAAW,CAAC;IAC9B,YAAY,EAAE,IAAA,mBAAG,EAAC,cAAc,CAAC;IACjC,SAAS,EAAE,IAAA,mBAAG,EAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,IAAA,mBAAG,EAAC,eAAe,CAAC;IACnC,eAAe,EAAE,IAAA,mBAAG,EAAC,iBAAiB,CAAC;IACvC,MAAM,EAAE,IAAA,sBAAM,EAAC,QAAQ,CAAC;IACxB,SAAS,EAAE,IAAA,wBAAQ,EAAC,WAAW,CAAC;IAChC,SAAS,EAAE,IAAA,wBAAQ,EAAC,WAAW,CAAC;CACjC,EACD;IACE,IAAI,EAAE,yBAAyB;IAC/B,IAAI,EAAE,YAAI,CAAC,KAAK;IAChB,SAAS,EAAE,sBAAc;IACzB,eAAe,EAAE,OAAO;CACzB,CACF,CAAC;AAKF;;GAEG;AACU,QAAA,cAAc,GAAG,IAAA,yBAAS,EAAC,aAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3D,QAAQ,EAAE,GAAG,CAAC,mBAAQ,EAAE;QACtB,MAAM,EAAE,CAAC,aAAK,CAAC,YAAY,CAAC;QAC5B,UAAU,EAAE,CAAC,mBAAQ,CAAC,EAAS,CAAC;KACjC,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ;;GAEG;AACU,QAAA,iBAAiB,GAAG,IAAA,yBAAS,EAAC,mBAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,MAAM,EAAE,IAAI,CAAC,aAAK,EAAE;QAClB,MAAM,EAAE,CAAC,mBAAQ,CAAC,QAAQ,CAAC;QAC3B,UAAU,EAAE,CAAC,aAAK,CAAC,EAAS,CAAC;KAC9B,CAAC;CACH,CAAC,CAAC,CAAC","sourcesContent":["/**\n * Model Schema - AI 模型配置\n *\n * 存储位置: /settings/ai/models.ttl\n *\n * RDF 示例:\n * <#text-embedding-004> a udfs:Model ;\n * udfs:displayName \"Google Text Embedding 004\" ;\n * udfs:modelType \"embedding\" ;\n * udfs:dimension 768 ;\n * udfs:status \"active\" ;\n * udfs:isProvidedBy </settings/ai/providers.ttl#google> .\n *\n * 注:id 即为模型名(如 text-embedding-004),与供应商 API 对齐\n */\n\nimport { podTable, string, int, datetime, uri, relations } from '@undefineds.co/drizzle-solid';\nimport { UDFS, UDFS_NAMESPACE } from '../../vocab';\nimport { Provider } from './provider';\n\n/**\n * Model - 基础模型定义\n */\nexport const Model = podTable(\n 'Model',\n {\n id: string('id').primaryKey(),\n displayName: string('displayName'),\n modelType: string('modelType'),\n isProvidedBy: uri('isProvidedBy'),\n dimension: int('dimension'),\n contextLength: int('contextLength'),\n maxOutputTokens: int('maxOutputTokens'),\n status: string('status'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/settings/ai/models.ttl',\n type: UDFS.Model,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '#{id}',\n },\n);\n\nexport type ModelRow = typeof Model.$inferSelect;\nexport type ModelInsert = typeof Model.$inferInsert;\n\n/**\n * Model -> Provider 关系定义\n */\nexport const ModelRelations = relations(Model, ({ one }) => ({\n provider: one(Provider, {\n fields: [Model.isProvidedBy],\n references: [Provider.id as any],\n }),\n}));\n\n/**\n * Provider -> Model 关系定义\n */\nexport const ProviderRelations = relations(Provider, ({ many }) => ({\n models: many(Model, {\n fields: [Provider.hasModel],\n references: [Model.id as any],\n }),\n}));\n"]}
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* udfs:baseUrl "https://generativelanguage.googleapis.com/v1beta/openai" ;
|
|
10
10
|
* udfs:hasModel </settings/ai/models.ttl#text-embedding-004> .
|
|
11
11
|
*/
|
|
12
|
-
export declare const Provider: import("drizzle-solid/dist/core/schema").PodTableWithColumns<import("drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
13
|
-
id: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
14
|
-
displayName: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
15
|
-
baseUrl: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
16
|
-
proxyUrl: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
17
|
-
hasModel: import("drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
18
|
-
hasCredential: import("drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
12
|
+
export declare const Provider: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
13
|
+
id: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
14
|
+
displayName: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
15
|
+
baseUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
16
|
+
proxyUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
17
|
+
hasModel: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
18
|
+
hasCredential: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
19
19
|
}>>;
|
|
20
20
|
export type ProviderRow = typeof Provider.$inferSelect;
|
|
21
21
|
export type ProviderInsert = typeof Provider.$inferInsert;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.Provider = void 0;
|
|
15
|
-
const drizzle_solid_1 = require("drizzle-solid");
|
|
15
|
+
const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
|
|
16
16
|
const vocab_1 = require("../../vocab");
|
|
17
17
|
exports.Provider = (0, drizzle_solid_1.podTable)('Provider', {
|
|
18
18
|
id: (0, drizzle_solid_1.string)('id').primaryKey(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/ai/schema/provider.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/ai/schema/provider.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,gEAAqE;AACrE,uCAAmD;AAEtC,QAAA,QAAQ,GAAG,IAAA,wBAAQ,EAC9B,UAAU,EACV;IACE,EAAE,EAAE,IAAA,sBAAM,EAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC7B,WAAW,EAAE,IAAA,sBAAM,EAAC,aAAa,CAAC;IAClC,OAAO,EAAE,IAAA,sBAAM,EAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,IAAA,sBAAM,EAAC,UAAU,CAAC;IAC5B,QAAQ,EAAE,IAAA,mBAAG,EAAC,UAAU,CAAC;IACzB,aAAa,EAAE,IAAA,mBAAG,EAAC,eAAe,CAAC;CACpC,EACD;IACE,IAAI,EAAE,4BAA4B;IAClC,IAAI,EAAE,YAAI,CAAC,QAAQ;IACnB,SAAS,EAAE,sBAAc;IACzB,eAAe,EAAE,OAAO;CACzB,CACF,CAAC","sourcesContent":["/**\n * Provider Schema - AI 供应商配置\n *\n * 存储位置: /settings/ai/providers.ttl\n *\n * RDF 示例:\n * <#google> a udfs:Provider ;\n * udfs:displayName \"Google AI\" ;\n * udfs:baseUrl \"https://generativelanguage.googleapis.com/v1beta/openai\" ;\n * udfs:hasModel </settings/ai/models.ttl#text-embedding-004> .\n */\n\nimport { podTable, string, uri } from '@undefineds.co/drizzle-solid';\nimport { UDFS, UDFS_NAMESPACE } from '../../vocab';\n\nexport const Provider = podTable(\n 'Provider',\n {\n id: string('id').primaryKey(),\n displayName: string('displayName'),\n baseUrl: string('baseUrl'),\n proxyUrl: string('proxyUrl'),\n hasModel: uri('hasModel'),\n hasCredential: uri('hasCredential'),\n },\n {\n base: '/settings/ai/providers.ttl',\n type: UDFS.Provider,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '#{id}',\n },\n);\n\nexport type ProviderRow = typeof Provider.$inferSelect;\nexport type ProviderInsert = typeof Provider.$inferInsert;\n"]}
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
* udfs:status "completed" ;
|
|
15
15
|
* udfs:createdAt "2024-01-15T10:30:00Z"^^xsd:dateTime .
|
|
16
16
|
*/
|
|
17
|
-
export declare const VectorStore: import("drizzle-solid/dist/core/schema").PodTableWithColumns<import("drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
18
|
-
id: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
19
|
-
name: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
20
|
-
container: import("drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
21
|
-
chunkingStrategy: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
22
|
-
status: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
23
|
-
createdAt: import("drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
24
|
-
lastActiveAt: import("drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
17
|
+
export declare const VectorStore: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
18
|
+
id: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
19
|
+
name: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
20
|
+
container: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
21
|
+
chunkingStrategy: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
22
|
+
status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
23
|
+
createdAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
24
|
+
lastActiveAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
25
25
|
}>>;
|
|
26
26
|
export type VectorStoreRow = typeof VectorStore.$inferSelect;
|
|
27
27
|
export type VectorStoreInsert = typeof VectorStore.$inferInsert;
|
|
@@ -39,15 +39,15 @@ export type VectorStoreInsert = typeof VectorStore.$inferInsert;
|
|
|
39
39
|
* udfs:usageBytes "1024"^^xsd:integer ;
|
|
40
40
|
* udfs:indexedAt "2024-01-15T10:30:00Z"^^xsd:dateTime .
|
|
41
41
|
*/
|
|
42
|
-
export declare const IndexedFile: import("drizzle-solid/dist/core/schema").PodTableWithColumns<import("drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
43
|
-
id: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
44
|
-
fileUrl: import("drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
45
|
-
vectorId: import("drizzle-solid/dist/core/schema").ColumnBuilder<"integer", null, false, false>;
|
|
46
|
-
chunkingStrategy: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
47
|
-
status: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
48
|
-
usageBytes: import("drizzle-solid/dist/core/schema").ColumnBuilder<"integer", null, false, false>;
|
|
49
|
-
lastError: import("drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
50
|
-
indexedAt: import("drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
42
|
+
export declare const IndexedFile: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
|
|
43
|
+
id: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
|
|
44
|
+
fileUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
|
|
45
|
+
vectorId: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"integer", null, false, false>;
|
|
46
|
+
chunkingStrategy: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
47
|
+
status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
48
|
+
usageBytes: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"integer", null, false, false>;
|
|
49
|
+
lastError: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
|
|
50
|
+
indexedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
|
|
51
51
|
}>>;
|
|
52
52
|
export type IndexedFileRow = typeof IndexedFile.$inferSelect;
|
|
53
53
|
export type IndexedFileInsert = typeof IndexedFile.$inferInsert;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.IndexedFile = exports.VectorStore = void 0;
|
|
9
|
-
const drizzle_solid_1 = require("drizzle-solid");
|
|
9
|
+
const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
|
|
10
10
|
const vocab_1 = require("../../vocab");
|
|
11
11
|
/**
|
|
12
12
|
* VectorStore - Container 级别的知识库配置
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../../../src/ai/schema/vector-store.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../../../src/ai/schema/vector-store.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,gEAAoF;AACpF,uCAAmD;AAEnD;;;;;;;;;;GAUG;AACU,QAAA,WAAW,GAAG,IAAA,wBAAQ,EACjC,aAAa,EACb;IACE,EAAE,EAAE,IAAA,sBAAM,EAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC7B,IAAI,EAAE,IAAA,sBAAM,EAAC,MAAM,CAAC;IACpB,SAAS,EAAE,IAAA,mBAAG,EAAC,WAAW,CAAC;IAC3B,gBAAgB,EAAE,IAAA,sBAAM,EAAC,kBAAkB,CAAC;IAC5C,MAAM,EAAE,IAAA,sBAAM,EAAC,QAAQ,CAAC;IACxB,SAAS,EAAE,IAAA,wBAAQ,EAAC,WAAW,CAAC;IAChC,YAAY,EAAE,IAAA,wBAAQ,EAAC,cAAc,CAAC;CACvC,EACD;IACE,IAAI,EAAE,gCAAgC;IACtC,IAAI,EAAE,YAAI,CAAC,WAAW;IACtB,SAAS,EAAE,sBAAc;IACzB,eAAe,EAAE,OAAO;CACzB,CACF,CAAC;AAKF;;;;;;;;;;;;;GAaG;AACU,QAAA,WAAW,GAAG,IAAA,wBAAQ,EACjC,aAAa,EACb;IACE,EAAE,EAAE,IAAA,sBAAM,EAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC7B,OAAO,EAAE,IAAA,mBAAG,EAAC,SAAS,CAAC;IACvB,QAAQ,EAAE,IAAA,mBAAG,EAAC,UAAU,CAAC;IACzB,gBAAgB,EAAE,IAAA,sBAAM,EAAC,kBAAkB,CAAC;IAC5C,MAAM,EAAE,IAAA,sBAAM,EAAC,QAAQ,CAAC;IACxB,UAAU,EAAE,IAAA,mBAAG,EAAC,YAAY,CAAC;IAC7B,SAAS,EAAE,IAAA,sBAAM,EAAC,WAAW,CAAC;IAC9B,SAAS,EAAE,IAAA,wBAAQ,EAAC,WAAW,CAAC;CACjC,EACD;IACE,IAAI,EAAE,gCAAgC;IACtC,IAAI,EAAE,YAAI,CAAC,WAAW;IACtB,SAAS,EAAE,sBAAc;IACzB,eAAe,EAAE,OAAO;CACzB,CACF,CAAC","sourcesContent":["/**\n * VectorStore Schema - Container 级别的知识库配置\n *\n * 存储位置: /settings/ai/vector-stores.ttl\n */\n\nimport { podTable, string, int, datetime, uri } from '@undefineds.co/drizzle-solid';\nimport { UDFS, UDFS_NAMESPACE } from '../../vocab';\n\n/**\n * VectorStore - Container 级别的知识库配置\n *\n * RDF 示例:\n * <#vs_abc123> a udfs:VectorStore ;\n * udfs:name \"My Notes\" ;\n * udfs:container </notes/> ;\n * udfs:chunkingStrategy \"auto\" ;\n * udfs:status \"completed\" ;\n * udfs:createdAt \"2024-01-15T10:30:00Z\"^^xsd:dateTime .\n */\nexport const VectorStore = podTable(\n 'VectorStore',\n {\n id: string('id').primaryKey(),\n name: string('name'),\n container: uri('container'),\n chunkingStrategy: string('chunkingStrategy'),\n status: string('status'),\n createdAt: datetime('createdAt'),\n lastActiveAt: datetime('lastActiveAt'),\n },\n {\n base: '/settings/ai/vector-stores.ttl',\n type: UDFS.VectorStore,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '#{id}',\n },\n);\n\nexport type VectorStoreRow = typeof VectorStore.$inferSelect;\nexport type VectorStoreInsert = typeof VectorStore.$inferInsert;\n\n/**\n * IndexedFile - 全局文件索引状态管理\n *\n * 存储位置: /settings/ai/indexed-files.ttl\n *\n * RDF 示例:\n * <#idx_abc123> a udfs:IndexedFile ;\n * udfs:fileUrl </notes/hello.md> ;\n * udfs:vectorId \"123456789\"^^xsd:integer ;\n * udfs:chunkingStrategy \"auto\" ;\n * udfs:status \"completed\" ;\n * udfs:usageBytes \"1024\"^^xsd:integer ;\n * udfs:indexedAt \"2024-01-15T10:30:00Z\"^^xsd:dateTime .\n */\nexport const IndexedFile = podTable(\n 'IndexedFile',\n {\n id: string('id').primaryKey(),\n fileUrl: uri('fileUrl'),\n vectorId: int('vectorId'),\n chunkingStrategy: string('chunkingStrategy'),\n status: string('status'),\n usageBytes: int('usageBytes'),\n lastError: string('lastError'),\n indexedAt: datetime('indexedAt'),\n },\n {\n base: '/settings/ai/indexed-files.ttl',\n type: UDFS.IndexedFile,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '#{id}',\n },\n);\n\nexport type IndexedFileRow = typeof IndexedFile.$inferSelect;\nexport type IndexedFileInsert = typeof IndexedFile.$inferInsert;\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CredentialReaderImpl = void 0;
|
|
4
4
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
5
|
-
const drizzle_solid_1 = require("drizzle-solid");
|
|
5
|
+
const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
|
|
6
6
|
const CredentialReader_1 = require("./CredentialReader");
|
|
7
7
|
const tables_1 = require("../../credential/schema/tables");
|
|
8
8
|
const provider_1 = require("../schema/provider");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CredentialReaderImpl.js","sourceRoot":"","sources":["../../../src/ai/service/CredentialReaderImpl.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,
|
|
1
|
+
{"version":3,"file":"CredentialReaderImpl.js","sourceRoot":"","sources":["../../../src/ai/service/CredentialReaderImpl.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,gEAAgE;AAChE,yDAAsD;AAEtD,2DAA4D;AAC5D,iDAA8C;AAC9C,yDAA8E;AAE9E,MAAM,MAAM,GAAG;IACb,UAAU,EAAE,mBAAU;IACtB,QAAQ,EAAE,mBAAQ;CACnB,CAAC;AAEF,MAAa,oBAAqB,SAAQ,mCAAgB;IAA1D;;QACqB,WAAM,GAAG,IAAA,oCAAY,EAAC,IAAI,CAAC,CAAC;IAiDjD,CAAC;IA/CiB,KAAK,CAAC,eAAe,CACnC,UAAkB,EAClB,UAAkB,EAClB,kBAAgC,EAChC,KAAc;QAEd,IAAI,CAAC;YACH,MAAM,OAAO,GAAG;gBACd,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE;gBACjC,KAAK,EAAE,kBAAkB;aAC1B,CAAC;YACF,MAAM,EAAE,GAAG,IAAA,uBAAO,EAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAExC,kBAAkB;YAClB,MAAM,WAAW,GAAG,GAAG,UAAU,6BAA6B,UAAU,EAAE,CAAC;YAE3E,qCAAqC;YACrC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC;gBACrD,KAAK,EAAE,IAAA,mBAAG,EACR,IAAA,kBAAE,EAAC,mBAAU,CAAC,OAAO,EAAE,mBAAW,CAAC,EAAE,CAAC,EACtC,IAAA,kBAAE,EAAC,mBAAU,CAAC,MAAM,EAAE,wBAAgB,CAAC,MAAM,CAAC,EAC9C,IAAA,kBAAE,EAAC,mBAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CACrC;gBACD,IAAI,EAAE;oBACJ,QAAQ,EAAE,IAAI;iBACf;aACF,CAAC,CAAC;YAEH,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,UAAU,EAAE,CAAC,CAAC;gBAC5E,OAAO,IAAI,CAAC;YACd,CAAC;YAED,eAAe;YACf,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAQ,CAAC;YAEtF,OAAO;gBACL,QAAQ,EAAE,UAAU;gBACpB,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,QAAQ,EAAE,OAAO,IAAI,SAAS;gBACxE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,QAAQ,IAAI,SAAS;aACrD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;YAClF,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAlDD,oDAkDC","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport { drizzle, eq, and } from '@undefineds.co/drizzle-solid';\nimport { CredentialReader } from './CredentialReader';\nimport type { AiCredential } from './types';\nimport { Credential } from '../../credential/schema/tables';\nimport { Provider } from '../schema/provider';\nimport { ServiceType, CredentialStatus } from '../../credential/schema/types';\n\nconst schema = {\n credential: Credential,\n provider: Provider,\n};\n\nexport class CredentialReaderImpl extends CredentialReader {\n protected readonly logger = getLoggerFor(this);\n\n public override async getAiCredential(\n podBaseUrl: string,\n providerId: string,\n authenticatedFetch: typeof fetch,\n webId?: string,\n ): Promise<AiCredential | null> {\n try {\n const session = {\n info: { isLoggedIn: true, webId },\n fetch: authenticatedFetch,\n };\n const db = drizzle(session, { schema });\n\n // 构建 Provider URI\n const providerUri = `${podBaseUrl}settings/ai/providers.ttl#${providerId}`;\n\n // 查询 credential,直接通过 provider URI 过滤\n const credentials = await db.query.credential.findMany({\n where: and(\n eq(Credential.service, ServiceType.AI),\n eq(Credential.status, CredentialStatus.ACTIVE),\n eq(Credential.provider, providerUri),\n ),\n with: {\n provider: true,\n },\n });\n\n if (credentials.length === 0) {\n this.logger.debug(`No active credential found for provider: ${providerId}`);\n return null;\n }\n\n // 随机选择一个(负载均衡)\n const credential = credentials[Math.floor(Math.random() * credentials.length)] as any;\n\n return {\n provider: providerId,\n apiKey: credential.apiKey,\n baseUrl: credential.baseUrl || credential.provider?.baseUrl || undefined,\n proxyUrl: credential.provider?.proxyUrl || undefined,\n };\n } catch (error) {\n this.logger.error(`Failed to read credential for provider ${providerId}:`, error);\n return null;\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultAiConfigService.js","sourceRoot":"","sources":["../../../src/ai/service/DefaultAiConfigService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 默认 AI 配置服务\n *\n * 当用户没有配置任何 AI provider 时,使用 OpenRouter 免费模型\n * 智能识别用户输入中的 AI 配置信息并存储到 Pod\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\nimport { drizzle } from 'drizzle-solid';\nimport { Provider } from '../schema/provider';\nimport { Model } from '../schema/model';\nimport { Credential } from '../../credential/schema/tables';\nimport { ServiceType, CredentialStatus } from '../../credential/schema/types';\n\nconst logger = getLoggerFor('DefaultAiConfigService');\n\nconst schema = {\n provider: Provider,\n model: Model,\n credential: Credential,\n};\n\n/**\n * 默认 AI 配置(OpenRouter 免费模型)\n */\nexport const DEFAULT_AI_CONFIG = {\n baseURL: 'https://openrouter.ai/api/v1',\n apiKey: '', // 用户需要填入自己的 OpenRouter API Key\n model: 'stepfun/step-3.5-flash:free',\n displayName: 'OpenRouter Free',\n};\n\n/**\n * 从用户输入中识别 AI 配置信息\n */\nexport interface ParsedAiConfig {\n provider?: string;\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n proxyUrl?: string;\n}\n\n/**\n * 识别用户输入中的 AI 配置信息\n */\nexport function parseAiConfigFromInput(input: string): ParsedAiConfig | null {\n const result: ParsedAiConfig = {};\n \n // 识别 API Key (常见的 key 格式)\n const apiKeyPatterns = [\n /(?:api[_-]?key|key|token|sk)[\\s:=]+[\"']?([a-zA-Z0-9_\\-]{20,})[\"']?/i,\n /(sk-[a-zA-Z0-9]{20,})/i,\n /([a-f0-9]{32,})/i,\n ];\n \n for (const pattern of apiKeyPatterns) {\n const match = input.match(pattern);\n if (match) {\n result.apiKey = match[1];\n break;\n }\n }\n \n // 识别 Provider / Base URL\n const providerPatterns: Record<string, RegExp[]> = {\n openai: [/openai\\.com/i, /api\\.openai\\.com/i],\n google: [/google.*gemini/i, /generativelanguage\\.googleapis\\.com/i, /gemini/i],\n anthropic: [/anthropic/i, /claude/i, /api\\.anthropic\\.com/i],\n deepseek: [/deepseek/i, /api\\.deepseek\\.com/i],\n openrouter: [/openrouter/i, /openrouter\\.ai/i],\n ollama: [/ollama/i, /localhost:11434/i],\n mistral: [/mistral/i, /api\\.mistral\\.ai/i],\n cohere: [/cohere/i, /api\\.cohere\\.ai/i],\n zhipu: [/zhipu/i, /智谱/i, /bigmodel/i],\n };\n \n for (const [name, patterns] of Object.entries(providerPatterns)) {\n for (const pattern of patterns) {\n if (pattern.test(input)) {\n result.provider = name;\n break;\n }\n }\n if (result.provider) break;\n }\n \n // 识别 Base URL\n const urlPattern = /(https?:\\/\\/[a-zA-Z0-9\\-\\.]+(?:\\:\\d+)?(?:\\/[a-zA-Z0-9\\-\\/]*)?)/i;\n const urlMatch = input.match(urlPattern);\n if (urlMatch) {\n result.baseUrl = urlMatch[1];\n }\n \n // 识别 Model\n const modelPatterns = [\n // OpenAI\n /gpt-4[\\w\\-]*/i,\n /gpt-3\\.5-turbo/i,\n // Google\n /gemini-[\\w\\-]+/i,\n // Anthropic\n /claude-[\\w\\-]+/i,\n // DeepSeek\n /deepseek-[\\w\\-]+/i,\n // Llama\n /llama-[\\d\\.]+[\\w\\-]*/i,\n // Mistral\n /mistral-[\\w\\-]+/i,\n // Qwen\n /qwen[\\w\\-]*/i,\n // 通用\n /[\"']([a-zA-Z0-9\\/\\-\\:]+free)[\"']/i,\n /model[:\\s=]+[\"']?([a-zA-Z0-9\\/\\-\\:_\\.]+)[\"']?/i,\n ];\n \n for (const pattern of modelPatterns) {\n const match = input.match(pattern);\n if (match) {\n result.model = match[0].replace(/[\"']/g, '');\n break;\n }\n }\n \n // 识别 Proxy URL\n const proxyPattern = /proxy[:\\s=]+[\"']?(https?:\\/\\/[^\"'\\s]+)[\"']?/i;\n const proxyMatch = input.match(proxyPattern);\n if (proxyMatch) {\n result.proxyUrl = proxyMatch[1];\n }\n \n // 如果什么都没识别到,返回 null\n if (Object.keys(result).length === 0) {\n return null;\n }\n \n logger.debug(`Parsed AI config from input: ${JSON.stringify(result)}`);\n return result;\n}\n\n/**\n * 获取完整的 AI 配置(默认 + 解析出的)\n */\nexport function getDefaultAiConfig(userInput?: string): {\n baseURL: string;\n apiKey: string;\n model: string;\n displayName: string;\n parsedConfig?: ParsedAiConfig;\n} {\n const parsedConfig = userInput ? parseAiConfigFromInput(userInput) : null;\n \n // 如果用户提供了 baseUrl,使用用户的;否则使用默认\n const baseURL = parsedConfig?.baseUrl || \n (parsedConfig?.provider ? getDefaultBaseUrl(parsedConfig.provider) : DEFAULT_AI_CONFIG.baseURL);\n \n // 使用用户提供的 API Key 或空字符串\n const apiKey = parsedConfig?.apiKey || DEFAULT_AI_CONFIG.apiKey;\n \n // 使用用户提供的模型或默认模型\n const model = parsedConfig?.model || DEFAULT_AI_CONFIG.model;\n \n return {\n baseURL,\n apiKey,\n model,\n displayName: parsedConfig?.provider \n ? `${parsedConfig.provider.charAt(0).toUpperCase() + parsedConfig.provider.slice(1)}` \n : DEFAULT_AI_CONFIG.displayName,\n parsedConfig: parsedConfig || undefined,\n };\n}\n\n/**\n * 根据 provider 名称获取默认 base URL\n */\nfunction getDefaultBaseUrl(provider: string): string {\n const urls: Record<string, string> = {\n openai: 'https://api.openai.com/v1',\n google: 'https://generativelanguage.googleapis.com/v1beta/openai',\n anthropic: 'https://api.anthropic.com/v1',\n deepseek: 'https://api.deepseek.com/v1',\n openrouter: 'https://openrouter.ai/api/v1',\n ollama: 'http://localhost:11434/v1',\n mistral: 'https://api.mistral.ai/v1',\n cohere: 'https://api.cohere.ai/v1',\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\n };\n return urls[provider.toLowerCase()] || DEFAULT_AI_CONFIG.baseURL;\n}\n\n/**\n * 保存 AI 配置到 Pod\n * \n * 注意:这是一个占位实现,实际的保存逻辑需要调用 PodChatKitStore 的方法\n * 或通过 API 端点进行保存\n */\nexport async function saveAiConfigToPod(\n _podBaseUrl: string,\n config: ParsedAiConfig,\n _authenticatedFetch: typeof fetch,\n _webId?: string,\n): Promise<{ success: boolean; message: string }> {\n const logger = getLoggerFor('DefaultAiConfigService');\n \n // 验证必要的字段\n if (!config.provider && !config.baseUrl) {\n return { success: false, message: '无法识别 AI Provider' };\n }\n if (!config.apiKey) {\n return { success: false, message: '无法识别 API Key' };\n }\n \n const providerId = config.provider || 'custom';\n const displayName = config.provider \n ? config.provider.charAt(0).toUpperCase() + config.provider.slice(1)\n : 'Custom Provider';\n \n logger.debug(`AI config parsed: provider=${providerId}, model=${config.model}`);\n \n return { \n success: true, \n message: `已识别 AI 配置:${displayName}${config.model ? ` (${config.model})` : ''}。请在设置中确认保存。` \n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DefaultAiConfigService.js","sourceRoot":"","sources":["../../../src/ai/service/DefaultAiConfigService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 默认 AI 配置服务\n *\n * 当用户没有配置任何 AI provider 时,使用 OpenRouter 免费模型\n * 智能识别用户输入中的 AI 配置信息并存储到 Pod\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\nimport { drizzle } from '@undefineds.co/drizzle-solid';\nimport { Provider } from '../schema/provider';\nimport { Model } from '../schema/model';\nimport { Credential } from '../../credential/schema/tables';\nimport { ServiceType, CredentialStatus } from '../../credential/schema/types';\n\nconst logger = getLoggerFor('DefaultAiConfigService');\n\nconst schema = {\n provider: Provider,\n model: Model,\n credential: Credential,\n};\n\n/**\n * 默认 AI 配置(OpenRouter 免费模型)\n */\nexport const DEFAULT_AI_CONFIG = {\n baseURL: 'https://openrouter.ai/api/v1',\n apiKey: '', // 用户需要填入自己的 OpenRouter API Key\n model: 'stepfun/step-3.5-flash:free',\n displayName: 'OpenRouter Free',\n};\n\n/**\n * 从用户输入中识别 AI 配置信息\n */\nexport interface ParsedAiConfig {\n provider?: string;\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n proxyUrl?: string;\n}\n\n/**\n * 识别用户输入中的 AI 配置信息\n */\nexport function parseAiConfigFromInput(input: string): ParsedAiConfig | null {\n const result: ParsedAiConfig = {};\n \n // 识别 API Key (常见的 key 格式)\n const apiKeyPatterns = [\n /(?:api[_-]?key|key|token|sk)[\\s:=]+[\"']?([a-zA-Z0-9_\\-]{20,})[\"']?/i,\n /(sk-[a-zA-Z0-9]{20,})/i,\n /([a-f0-9]{32,})/i,\n ];\n \n for (const pattern of apiKeyPatterns) {\n const match = input.match(pattern);\n if (match) {\n result.apiKey = match[1];\n break;\n }\n }\n \n // 识别 Provider / Base URL\n const providerPatterns: Record<string, RegExp[]> = {\n openai: [/openai\\.com/i, /api\\.openai\\.com/i],\n google: [/google.*gemini/i, /generativelanguage\\.googleapis\\.com/i, /gemini/i],\n anthropic: [/anthropic/i, /claude/i, /api\\.anthropic\\.com/i],\n deepseek: [/deepseek/i, /api\\.deepseek\\.com/i],\n openrouter: [/openrouter/i, /openrouter\\.ai/i],\n ollama: [/ollama/i, /localhost:11434/i],\n mistral: [/mistral/i, /api\\.mistral\\.ai/i],\n cohere: [/cohere/i, /api\\.cohere\\.ai/i],\n zhipu: [/zhipu/i, /智谱/i, /bigmodel/i],\n };\n \n for (const [name, patterns] of Object.entries(providerPatterns)) {\n for (const pattern of patterns) {\n if (pattern.test(input)) {\n result.provider = name;\n break;\n }\n }\n if (result.provider) break;\n }\n \n // 识别 Base URL\n const urlPattern = /(https?:\\/\\/[a-zA-Z0-9\\-\\.]+(?:\\:\\d+)?(?:\\/[a-zA-Z0-9\\-\\/]*)?)/i;\n const urlMatch = input.match(urlPattern);\n if (urlMatch) {\n result.baseUrl = urlMatch[1];\n }\n \n // 识别 Model\n const modelPatterns = [\n // OpenAI\n /gpt-4[\\w\\-]*/i,\n /gpt-3\\.5-turbo/i,\n // Google\n /gemini-[\\w\\-]+/i,\n // Anthropic\n /claude-[\\w\\-]+/i,\n // DeepSeek\n /deepseek-[\\w\\-]+/i,\n // Llama\n /llama-[\\d\\.]+[\\w\\-]*/i,\n // Mistral\n /mistral-[\\w\\-]+/i,\n // Qwen\n /qwen[\\w\\-]*/i,\n // 通用\n /[\"']([a-zA-Z0-9\\/\\-\\:]+free)[\"']/i,\n /model[:\\s=]+[\"']?([a-zA-Z0-9\\/\\-\\:_\\.]+)[\"']?/i,\n ];\n \n for (const pattern of modelPatterns) {\n const match = input.match(pattern);\n if (match) {\n result.model = match[0].replace(/[\"']/g, '');\n break;\n }\n }\n \n // 识别 Proxy URL\n const proxyPattern = /proxy[:\\s=]+[\"']?(https?:\\/\\/[^\"'\\s]+)[\"']?/i;\n const proxyMatch = input.match(proxyPattern);\n if (proxyMatch) {\n result.proxyUrl = proxyMatch[1];\n }\n \n // 如果什么都没识别到,返回 null\n if (Object.keys(result).length === 0) {\n return null;\n }\n \n logger.debug(`Parsed AI config from input: ${JSON.stringify(result)}`);\n return result;\n}\n\n/**\n * 获取完整的 AI 配置(默认 + 解析出的)\n */\nexport function getDefaultAiConfig(userInput?: string): {\n baseURL: string;\n apiKey: string;\n model: string;\n displayName: string;\n parsedConfig?: ParsedAiConfig;\n} {\n const parsedConfig = userInput ? parseAiConfigFromInput(userInput) : null;\n \n // 如果用户提供了 baseUrl,使用用户的;否则使用默认\n const baseURL = parsedConfig?.baseUrl || \n (parsedConfig?.provider ? getDefaultBaseUrl(parsedConfig.provider) : DEFAULT_AI_CONFIG.baseURL);\n \n // 使用用户提供的 API Key 或空字符串\n const apiKey = parsedConfig?.apiKey || DEFAULT_AI_CONFIG.apiKey;\n \n // 使用用户提供的模型或默认模型\n const model = parsedConfig?.model || DEFAULT_AI_CONFIG.model;\n \n return {\n baseURL,\n apiKey,\n model,\n displayName: parsedConfig?.provider \n ? `${parsedConfig.provider.charAt(0).toUpperCase() + parsedConfig.provider.slice(1)}` \n : DEFAULT_AI_CONFIG.displayName,\n parsedConfig: parsedConfig || undefined,\n };\n}\n\n/**\n * 根据 provider 名称获取默认 base URL\n */\nfunction getDefaultBaseUrl(provider: string): string {\n const urls: Record<string, string> = {\n openai: 'https://api.openai.com/v1',\n google: 'https://generativelanguage.googleapis.com/v1beta/openai',\n anthropic: 'https://api.anthropic.com/v1',\n deepseek: 'https://api.deepseek.com/v1',\n openrouter: 'https://openrouter.ai/api/v1',\n ollama: 'http://localhost:11434/v1',\n mistral: 'https://api.mistral.ai/v1',\n cohere: 'https://api.cohere.ai/v1',\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\n };\n return urls[provider.toLowerCase()] || DEFAULT_AI_CONFIG.baseURL;\n}\n\n/**\n * 保存 AI 配置到 Pod\n * \n * 注意:这是一个占位实现,实际的保存逻辑需要调用 PodChatKitStore 的方法\n * 或通过 API 端点进行保存\n */\nexport async function saveAiConfigToPod(\n _podBaseUrl: string,\n config: ParsedAiConfig,\n _authenticatedFetch: typeof fetch,\n _webId?: string,\n): Promise<{ success: boolean; message: string }> {\n const logger = getLoggerFor('DefaultAiConfigService');\n \n // 验证必要的字段\n if (!config.provider && !config.baseUrl) {\n return { success: false, message: '无法识别 AI Provider' };\n }\n if (!config.apiKey) {\n return { success: false, message: '无法识别 API Key' };\n }\n \n const providerId = config.provider || 'custom';\n const displayName = config.provider \n ? config.provider.charAt(0).toUpperCase() + config.provider.slice(1)\n : 'Custom Provider';\n \n logger.debug(`AI config parsed: provider=${providerId}, model=${config.model}`);\n \n return { \n success: true, \n message: `已识别 AI 配置:${displayName}${config.model ? ` (${config.model})` : ''}。请在设置中确认保存。` \n };\n}\n"]}
|
package/dist/api/ApiServer.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export interface Route {
|
|
|
16
16
|
public?: boolean;
|
|
17
17
|
}
|
|
18
18
|
export interface ApiServerOptions {
|
|
19
|
-
port
|
|
19
|
+
port?: number;
|
|
20
20
|
host?: string;
|
|
21
|
+
socketPath?: string;
|
|
21
22
|
authMiddleware: AuthMiddleware;
|
|
22
23
|
corsOrigins?: string[];
|
|
23
24
|
}
|
|
@@ -28,6 +29,7 @@ export declare class ApiServer {
|
|
|
28
29
|
private readonly logger;
|
|
29
30
|
private readonly port;
|
|
30
31
|
private readonly host;
|
|
32
|
+
private readonly socketPath?;
|
|
31
33
|
private readonly authMiddleware;
|
|
32
34
|
private readonly corsOrigins;
|
|
33
35
|
private readonly routes;
|
package/dist/api/ApiServer.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ApiServer = void 0;
|
|
4
4
|
const node_http_1 = require("node:http");
|
|
5
5
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
6
|
+
const socket_utils_1 = require("../runtime/socket-utils");
|
|
6
7
|
/**
|
|
7
8
|
* Standalone API Server
|
|
8
9
|
*/
|
|
@@ -10,8 +11,9 @@ class ApiServer {
|
|
|
10
11
|
constructor(options) {
|
|
11
12
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
12
13
|
this.routes = [];
|
|
13
|
-
this.port = options.port;
|
|
14
|
+
this.port = options.port ?? 0;
|
|
14
15
|
this.host = options.host ?? '0.0.0.0';
|
|
16
|
+
this.socketPath = options.socketPath;
|
|
15
17
|
this.authMiddleware = options.authMiddleware;
|
|
16
18
|
this.corsOrigins = options.corsOrigins ?? ['*'];
|
|
17
19
|
}
|
|
@@ -63,6 +65,14 @@ class ApiServer {
|
|
|
63
65
|
});
|
|
64
66
|
});
|
|
65
67
|
this.server.on('error', reject);
|
|
68
|
+
if (this.socketPath) {
|
|
69
|
+
(0, socket_utils_1.prepareSocketPath)(this.socketPath);
|
|
70
|
+
this.server.listen(this.socketPath, () => {
|
|
71
|
+
this.logger.info(`API Server listening on unix://${this.socketPath}`);
|
|
72
|
+
resolve();
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
66
76
|
this.server.listen(this.port, this.host, () => {
|
|
67
77
|
this.logger.info(`API Server listening on ${this.host}:${this.port}`);
|
|
68
78
|
resolve();
|
|
@@ -83,6 +93,9 @@ class ApiServer {
|
|
|
83
93
|
reject(error);
|
|
84
94
|
}
|
|
85
95
|
else {
|
|
96
|
+
if (this.socketPath) {
|
|
97
|
+
(0, socket_utils_1.removeSocketPath)(this.socketPath);
|
|
98
|
+
}
|
|
86
99
|
this.logger.info('API Server stopped');
|
|
87
100
|
resolve();
|
|
88
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiServer.js","sourceRoot":"","sources":["../../src/api/ApiServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthMiddleware, AuthenticatedRequest } from './middleware/AuthMiddleware';\n\n/**\n * Route handler function\n */\nexport type RouteHandler = (\n request: AuthenticatedRequest,\n response: ServerResponse,\n params: Record<string, string>,\n) => Promise<void>;\n\n/**\n * Route definition\n */\nexport interface Route {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n /** If true, skip authentication */\n public?: boolean;\n}\n\nexport interface ApiServerOptions {\n port: number;\n host?: string;\n authMiddleware: AuthMiddleware;\n corsOrigins?: string[];\n}\n\n/**\n * Standalone API Server\n */\nexport class ApiServer {\n private readonly logger = getLoggerFor(this);\n private readonly port: number;\n private readonly host: string;\n private readonly authMiddleware: AuthMiddleware;\n private readonly corsOrigins: string[];\n private readonly routes: Route[] = [];\n private server?: Server;\n\n public constructor(options: ApiServerOptions) {\n this.port = options.port;\n this.host = options.host ?? '0.0.0.0';\n this.authMiddleware = options.authMiddleware;\n this.corsOrigins = options.corsOrigins ?? ['*'];\n }\n\n /**\n * Register a route\n */\n public route(\n method: string,\n path: string,\n handler: RouteHandler,\n options?: {\n /** If true, skip authentication for this route */\n public?: boolean;\n },\n ): void {\n const { pattern, paramNames } = this.pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n public: options?.public,\n });\n this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}`);\n }\n\n /**\n * Convenience methods for common HTTP methods\n */\n public get(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('GET', path, handler, options);\n }\n\n public post(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('POST', path, handler, options);\n }\n\n public put(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PUT', path, handler, options);\n }\n\n public delete(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('DELETE', path, handler, options);\n }\n\n public patch(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PATCH', path, handler, options);\n }\n\n /**\n * Start the server\n */\n public async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((error) => {\n this.logger.error(`Unhandled error: ${error}`);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n });\n });\n\n this.server.on('error', reject);\n\n this.server.listen(this.port, this.host, () => {\n this.logger.info(`API Server listening on ${this.host}:${this.port}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n public async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n resolve();\n return;\n }\n\n this.server.close((error) => {\n if (error) {\n reject(error);\n } else {\n this.logger.info('API Server stopped');\n resolve();\n }\n });\n });\n }\n\n /**\n * Get the underlying HTTP server (for WebSocket upgrade)\n */\n public getHttpServer(): Server | undefined {\n return this.server;\n }\n\n private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const method = request.method?.toUpperCase() ?? 'GET';\n const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n\n // Handle CORS preflight\n if (method === 'OPTIONS') {\n this.handleCors(request, response);\n response.statusCode = 204;\n response.end();\n return;\n }\n\n // Add CORS headers\n this.handleCors(request, response);\n\n // Find matching route\n const match = this.findRoute(method, path);\n if (!match) {\n response.statusCode = 404;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Not Found' }));\n return;\n }\n\n const { route, params } = match;\n const authRequest = request as AuthenticatedRequest;\n\n // Run auth middleware unless route is public\n if (!route.public) {\n const authOk = await this.authMiddleware.process(authRequest, response);\n if (!authOk) {\n return;\n }\n }\n\n // Execute handler\n try {\n await route.handler(authRequest, response, params);\n } catch (error) {\n this.logger.error(`Route handler error: ${error}`);\n if (!response.headersSent) {\n response.statusCode = 500;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n }\n\n private findRoute(method: string, path: string): { route: Route; params: Record<string, string> } | undefined {\n for (const route of this.routes) {\n if (route.method !== method) {\n continue;\n }\n\n const match = route.pattern.exec(path);\n if (match) {\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, index) => {\n params[name] = match[index + 1];\n });\n return { route, params };\n }\n }\n return undefined;\n }\n\n private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n let regexStr = path\n // 先处理通配符 *path 或 * (匹配剩余所有路径)\n .replace(/\\*([a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name || 'wildcard');\n return '(.*)';\n })\n // 再处理普通参数 :param (只匹配单段)\n .replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n })\n .replace(/\\//g, '\\\\/');\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n }\n\n private handleCors(request: IncomingMessage, response: ServerResponse): void {\n const origin = request.headers.origin;\n\n if (this.corsOrigins.includes('*')) {\n response.setHeader('Access-Control-Allow-Origin', '*');\n } else if (origin && this.corsOrigins.includes(origin)) {\n response.setHeader('Access-Control-Allow-Origin', origin);\n response.setHeader('Vary', 'Origin');\n }\n\n response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n response.setHeader('Access-Control-Allow-Headers', '*');\n response.setHeader('Access-Control-Max-Age', '86400');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ApiServer.js","sourceRoot":"","sources":["../../src/api/ApiServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthMiddleware, AuthenticatedRequest } from './middleware/AuthMiddleware';\nimport { prepareSocketPath, removeSocketPath } from '../runtime/socket-utils';\n\n/**\n * Route handler function\n */\nexport type RouteHandler = (\n request: AuthenticatedRequest,\n response: ServerResponse,\n params: Record<string, string>,\n) => Promise<void>;\n\n/**\n * Route definition\n */\nexport interface Route {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n /** If true, skip authentication */\n public?: boolean;\n}\n\nexport interface ApiServerOptions {\n port?: number;\n host?: string;\n socketPath?: string;\n authMiddleware: AuthMiddleware;\n corsOrigins?: string[];\n}\n\n/**\n * Standalone API Server\n */\nexport class ApiServer {\n private readonly logger = getLoggerFor(this);\n private readonly port: number;\n private readonly host: string;\n private readonly socketPath?: string;\n private readonly authMiddleware: AuthMiddleware;\n private readonly corsOrigins: string[];\n private readonly routes: Route[] = [];\n private server?: Server;\n\n public constructor(options: ApiServerOptions) {\n this.port = options.port ?? 0;\n this.host = options.host ?? '0.0.0.0';\n this.socketPath = options.socketPath;\n this.authMiddleware = options.authMiddleware;\n this.corsOrigins = options.corsOrigins ?? ['*'];\n }\n\n /**\n * Register a route\n */\n public route(\n method: string,\n path: string,\n handler: RouteHandler,\n options?: {\n /** If true, skip authentication for this route */\n public?: boolean;\n },\n ): void {\n const { pattern, paramNames } = this.pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n public: options?.public,\n });\n this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}`);\n }\n\n /**\n * Convenience methods for common HTTP methods\n */\n public get(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('GET', path, handler, options);\n }\n\n public post(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('POST', path, handler, options);\n }\n\n public put(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PUT', path, handler, options);\n }\n\n public delete(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('DELETE', path, handler, options);\n }\n\n public patch(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PATCH', path, handler, options);\n }\n\n /**\n * Start the server\n */\n public async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((error) => {\n this.logger.error(`Unhandled error: ${error}`);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n });\n });\n\n this.server.on('error', reject);\n\n if (this.socketPath) {\n prepareSocketPath(this.socketPath);\n this.server.listen(this.socketPath, () => {\n this.logger.info(`API Server listening on unix://${this.socketPath}`);\n resolve();\n });\n return;\n }\n\n this.server.listen(this.port, this.host, () => {\n this.logger.info(`API Server listening on ${this.host}:${this.port}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n public async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n resolve();\n return;\n }\n\n this.server.close((error) => {\n if (error) {\n reject(error);\n } else {\n if (this.socketPath) {\n removeSocketPath(this.socketPath);\n }\n this.logger.info('API Server stopped');\n resolve();\n }\n });\n });\n }\n\n /**\n * Get the underlying HTTP server (for WebSocket upgrade)\n */\n public getHttpServer(): Server | undefined {\n return this.server;\n }\n\n private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const method = request.method?.toUpperCase() ?? 'GET';\n const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n\n // Handle CORS preflight\n if (method === 'OPTIONS') {\n this.handleCors(request, response);\n response.statusCode = 204;\n response.end();\n return;\n }\n\n // Add CORS headers\n this.handleCors(request, response);\n\n // Find matching route\n const match = this.findRoute(method, path);\n if (!match) {\n response.statusCode = 404;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Not Found' }));\n return;\n }\n\n const { route, params } = match;\n const authRequest = request as AuthenticatedRequest;\n\n // Run auth middleware unless route is public\n if (!route.public) {\n const authOk = await this.authMiddleware.process(authRequest, response);\n if (!authOk) {\n return;\n }\n }\n\n // Execute handler\n try {\n await route.handler(authRequest, response, params);\n } catch (error) {\n this.logger.error(`Route handler error: ${error}`);\n if (!response.headersSent) {\n response.statusCode = 500;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n }\n\n private findRoute(method: string, path: string): { route: Route; params: Record<string, string> } | undefined {\n for (const route of this.routes) {\n if (route.method !== method) {\n continue;\n }\n\n const match = route.pattern.exec(path);\n if (match) {\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, index) => {\n params[name] = match[index + 1];\n });\n return { route, params };\n }\n }\n return undefined;\n }\n\n private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n let regexStr = path\n // 先处理通配符 *path 或 * (匹配剩余所有路径)\n .replace(/\\*([a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name || 'wildcard');\n return '(.*)';\n })\n // 再处理普通参数 :param (只匹配单段)\n .replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n })\n .replace(/\\//g, '\\\\/');\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n }\n\n private handleCors(request: IncomingMessage, response: ServerResponse): void {\n const origin = request.headers.origin;\n\n if (this.corsOrigins.includes('*')) {\n response.setHeader('Access-Control-Allow-Origin', '*');\n } else if (origin && this.corsOrigins.includes(origin)) {\n response.setHeader('Access-Control-Allow-Origin', origin);\n response.setHeader('Vary', 'Origin');\n }\n\n response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n response.setHeader('Access-Control-Allow-Headers', '*');\n response.setHeader('Access-Control-Max-Age', '86400');\n }\n}\n"]}
|
|
@@ -36,6 +36,7 @@ export declare class PodChatKitStore implements ChatKitStore<StoreContext> {
|
|
|
36
36
|
* 获取认证后的 drizzle 实例 (缓存到 context 中)
|
|
37
37
|
*/
|
|
38
38
|
private getDb;
|
|
39
|
+
private getClientCredentialsAccessToken;
|
|
39
40
|
private createAccessTokenFetch;
|
|
40
41
|
/**
|
|
41
42
|
* 从 context 获取 webId
|
|
@@ -71,6 +72,11 @@ export declare class PodChatKitStore implements ChatKitStore<StoreContext> {
|
|
|
71
72
|
* 从 Thread 获取 chatId(提取纯 ID)
|
|
72
73
|
*/
|
|
73
74
|
private extractChatId;
|
|
75
|
+
private extractThreadId;
|
|
76
|
+
private getPodBaseUrl;
|
|
77
|
+
private buildThreadUri;
|
|
78
|
+
private selectMessagesForThread;
|
|
79
|
+
private deleteMessageRecord;
|
|
74
80
|
/**
|
|
75
81
|
* 获取 Thread 的 chatId
|
|
76
82
|
* 先从缓存获取,如果没有再查询数据库
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.PodChatKitStore = void 0;
|
|
16
|
-
const
|
|
17
|
-
const drizzle_solid_1 = require("drizzle-solid");
|
|
16
|
+
const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
|
|
18
17
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
19
18
|
const types_1 = require("./types");
|
|
20
19
|
const schema_1 = require("./schema");
|
|
@@ -94,37 +93,30 @@ class PodChatKitStore {
|
|
|
94
93
|
this.logger.warn('No accessToken and no valid client credentials in context, cannot access Pod');
|
|
95
94
|
return null;
|
|
96
95
|
}
|
|
97
|
-
// Fallback path:
|
|
96
|
+
// Fallback path: exchange client credentials for an access token directly.
|
|
98
97
|
this.logger.info(`[getDb] Using client credentials path for clientId: ${auth.clientId}`);
|
|
99
|
-
const session = new solid_client_authn_node_1.Session();
|
|
100
98
|
try {
|
|
101
|
-
await
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
tokenType: 'DPoP',
|
|
106
|
-
});
|
|
107
|
-
if (!session.info.isLoggedIn || !session.info.webId) {
|
|
108
|
-
throw new Error('Login failed');
|
|
99
|
+
const token = await this.getClientCredentialsAccessToken(auth.clientId, auth.clientSecret);
|
|
100
|
+
const webId = auth.webId ?? this.getWebId(context);
|
|
101
|
+
if (!webId) {
|
|
102
|
+
throw new Error('Missing webId for client credentials auth');
|
|
109
103
|
}
|
|
110
|
-
this.logger.info(`[getDb]
|
|
111
|
-
const authFetch =
|
|
112
|
-
const db = (0, drizzle_solid_1.drizzle)({ fetch: authFetch, info: { webId
|
|
113
|
-
|
|
114
|
-
this.logger.info(`Initializing tables for Pod: ${session.info.webId}`);
|
|
104
|
+
this.logger.info(`[getDb] Client credentials token acquired, webId: ${webId}`);
|
|
105
|
+
const authFetch = this.createAccessTokenFetch(token.accessToken, token.tokenType);
|
|
106
|
+
const db = (0, drizzle_solid_1.drizzle)({ fetch: authFetch, info: { webId, isLoggedIn: true } }, { schema });
|
|
107
|
+
this.logger.info(`Initializing tables for Pod: ${webId}`);
|
|
115
108
|
try {
|
|
116
109
|
await db.init([schema_1.Chat, schema_1.Thread, schema_1.Message]);
|
|
117
110
|
this.logger.info('Tables initialized successfully');
|
|
118
111
|
}
|
|
119
112
|
catch (initError) {
|
|
120
113
|
this.logger.error(`Failed to init tables: ${initError}`);
|
|
121
|
-
// 继续执行,可能容器已存在
|
|
122
114
|
}
|
|
123
|
-
// Cache both db and session.fetch in context for reuse
|
|
124
115
|
context._cachedDb = db;
|
|
125
116
|
context._cachedFetch = authFetch;
|
|
126
|
-
context._cachedWebId =
|
|
127
|
-
context.
|
|
117
|
+
context._cachedWebId = webId;
|
|
118
|
+
context._cachedAccessToken = token.accessToken;
|
|
119
|
+
context._cachedTokenType = token.tokenType;
|
|
128
120
|
return db;
|
|
129
121
|
}
|
|
130
122
|
catch (error) {
|
|
@@ -132,6 +124,30 @@ class PodChatKitStore {
|
|
|
132
124
|
return null;
|
|
133
125
|
}
|
|
134
126
|
}
|
|
127
|
+
async getClientCredentialsAccessToken(clientId, clientSecret) {
|
|
128
|
+
const response = await fetch(this.tokenEndpoint, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: {
|
|
131
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
132
|
+
},
|
|
133
|
+
body: new URLSearchParams({
|
|
134
|
+
grant_type: 'client_credentials',
|
|
135
|
+
client_id: clientId,
|
|
136
|
+
client_secret: clientSecret,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
throw new Error(`Client credentials token request failed: ${response.status} ${await response.text().catch(() => '')}`);
|
|
141
|
+
}
|
|
142
|
+
const token = await response.json();
|
|
143
|
+
if (!token.access_token) {
|
|
144
|
+
throw new Error(`Client credentials token response missing access_token: ${JSON.stringify(token)}`);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
accessToken: token.access_token,
|
|
148
|
+
tokenType: token.token_type?.toUpperCase() === 'DPOP' ? 'DPoP' : 'Bearer',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
135
151
|
createAccessTokenFetch(accessToken, tokenType) {
|
|
136
152
|
const scheme = tokenType ?? 'Bearer';
|
|
137
153
|
return async (input, init) => {
|
|
@@ -278,6 +294,42 @@ class PodChatKitStore {
|
|
|
278
294
|
}
|
|
279
295
|
return chatIdOrUri;
|
|
280
296
|
}
|
|
297
|
+
extractThreadId(threadIdOrUri) {
|
|
298
|
+
if (!threadIdOrUri)
|
|
299
|
+
return undefined;
|
|
300
|
+
if (threadIdOrUri.includes('#')) {
|
|
301
|
+
return threadIdOrUri.split('#').pop() || undefined;
|
|
302
|
+
}
|
|
303
|
+
return threadIdOrUri;
|
|
304
|
+
}
|
|
305
|
+
getPodBaseUrl(context) {
|
|
306
|
+
const cachedWebId = context._cachedWebId;
|
|
307
|
+
const webId = cachedWebId ?? this.getWebId(context);
|
|
308
|
+
if (!webId) {
|
|
309
|
+
throw new Error('Missing webId for Pod URI resolution');
|
|
310
|
+
}
|
|
311
|
+
return webId.replace('/profile/card#me', '').replace(/\/$/, '');
|
|
312
|
+
}
|
|
313
|
+
buildThreadUri(chatId, threadId, context) {
|
|
314
|
+
return `${this.getPodBaseUrl(context)}/.data/chat/${chatId}/index.ttl#${threadId}`;
|
|
315
|
+
}
|
|
316
|
+
async selectMessagesForThread(threadId, context) {
|
|
317
|
+
const db = await this.getDb(context);
|
|
318
|
+
if (!db) {
|
|
319
|
+
throw new Error('Cannot access Pod: invalid credentials');
|
|
320
|
+
}
|
|
321
|
+
const chatId = await this.getThreadChatId(threadId, context);
|
|
322
|
+
const messages = await db.select().from(schema_1.Message);
|
|
323
|
+
return messages.filter((message) => this.extractChatId(message.chatId) === chatId && this.extractThreadId(message.threadId) === threadId);
|
|
324
|
+
}
|
|
325
|
+
async deleteMessageRecord(db, message) {
|
|
326
|
+
const messageIri = message['@id'];
|
|
327
|
+
if (messageIri) {
|
|
328
|
+
await db.delete(schema_1.Message).whereByIri(messageIri);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
await db.delete(schema_1.Message).where((0, drizzle_solid_1.and)((0, drizzle_solid_1.eq)(schema_1.Message.id, message.id), (0, drizzle_solid_1.eq)(schema_1.Message.chatId, this.extractChatId(message.chatId)), (0, drizzle_solid_1.eq)(schema_1.Message.createdAt, message.createdAt)));
|
|
332
|
+
}
|
|
281
333
|
/**
|
|
282
334
|
* 获取 Thread 的 chatId
|
|
283
335
|
* 先从缓存获取,如果没有再查询数据库
|
|
@@ -476,7 +528,10 @@ class PodChatKitStore {
|
|
|
476
528
|
}
|
|
477
529
|
// 删除关联到此 Thread 的消息
|
|
478
530
|
try {
|
|
479
|
-
await
|
|
531
|
+
const messages = await this.selectMessagesForThread(threadId, context);
|
|
532
|
+
for (const message of messages) {
|
|
533
|
+
await this.deleteMessageRecord(db, message);
|
|
534
|
+
}
|
|
480
535
|
}
|
|
481
536
|
catch (err) {
|
|
482
537
|
if (!err.message?.includes('404') && !err.message?.includes('Could not retrieve') && !err.message?.includes('Parse error')) {
|
|
@@ -510,13 +565,8 @@ class PodChatKitStore {
|
|
|
510
565
|
// Thread Item Operations (ChatKit items = our Messages)
|
|
511
566
|
// =========================================================================
|
|
512
567
|
async loadThreadItems(threadId, after, limit, order, context) {
|
|
513
|
-
const db = await this.getDb(context);
|
|
514
|
-
if (!db) {
|
|
515
|
-
return { data: [], has_more: false };
|
|
516
|
-
}
|
|
517
568
|
try {
|
|
518
|
-
|
|
519
|
-
const messages = await db.select().from(schema_1.Message).where((0, drizzle_solid_1.eq)(schema_1.Message.threadId, threadId));
|
|
569
|
+
const messages = await this.selectMessagesForThread(threadId, context);
|
|
520
570
|
// 排序
|
|
521
571
|
messages.sort((a, b) => {
|
|
522
572
|
const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
@@ -580,7 +630,7 @@ class PodChatKitStore {
|
|
|
580
630
|
const messageRecord = {
|
|
581
631
|
id: item.id,
|
|
582
632
|
chatId, // 用于路径构建
|
|
583
|
-
threadId, // 关联到 Thread
|
|
633
|
+
threadId: this.buildThreadUri(chatId, threadId, context), // 关联到 Thread
|
|
584
634
|
maker: role === schema_1.MessageRole.USER ? webId : null,
|
|
585
635
|
role,
|
|
586
636
|
content,
|
|
@@ -707,11 +757,7 @@ WHERE { ${deletePatterns.join(' ')} }
|
|
|
707
757
|
}
|
|
708
758
|
}
|
|
709
759
|
async loadItem(threadId, itemId, context) {
|
|
710
|
-
const
|
|
711
|
-
if (!db) {
|
|
712
|
-
throw new Error('Cannot access Pod: invalid credentials');
|
|
713
|
-
}
|
|
714
|
-
const messages = await db.select().from(schema_1.Message).where((0, drizzle_solid_1.and)((0, drizzle_solid_1.eq)(schema_1.Message.id, itemId), (0, drizzle_solid_1.eq)(schema_1.Message.threadId, threadId)));
|
|
760
|
+
const messages = (await this.selectMessagesForThread(threadId, context)).filter((message) => message.id === itemId);
|
|
715
761
|
if (messages.length === 0) {
|
|
716
762
|
throw new Error(`Item not found: ${itemId}`);
|
|
717
763
|
}
|
|
@@ -722,7 +768,12 @@ WHERE { ${deletePatterns.join(' ')} }
|
|
|
722
768
|
if (!db) {
|
|
723
769
|
throw new Error('Cannot access Pod: invalid credentials');
|
|
724
770
|
}
|
|
725
|
-
|
|
771
|
+
const messages = await this.selectMessagesForThread(threadId, context);
|
|
772
|
+
const target = messages.find((message) => message.id === itemId);
|
|
773
|
+
if (!target) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
await this.deleteMessageRecord(db, target);
|
|
726
777
|
}
|
|
727
778
|
// =========================================================================
|
|
728
779
|
// Attachment Operations (存 Pod 文件)
|