@spaceflow/core 0.1.3 → 0.3.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/dist/index.js +2642 -3247
- package/dist/index.js.map +1 -1
- package/package.json +2 -13
- package/src/config/config-loader.ts +5 -6
- package/src/config/config-reader.service.ts +6 -11
- package/src/config/config-reader.ts +75 -0
- package/src/config/index.ts +4 -1
- package/src/config/load-env.ts +15 -0
- package/src/config/schema-generator.service.ts +0 -2
- package/src/config/spaceflow.config.ts +7 -20
- package/src/extension-system/define-extension.ts +25 -0
- package/src/extension-system/extension.interface.ts +0 -63
- package/src/extension-system/index.ts +5 -0
- package/src/extension-system/types.ts +201 -0
- package/src/index.ts +15 -18
- package/src/shared/claude-setup/claude-setup.service.ts +3 -8
- package/src/shared/claude-setup/index.ts +0 -1
- package/src/shared/feishu-sdk/feishu-sdk.service.ts +33 -21
- package/src/shared/feishu-sdk/fieshu-card.service.ts +9 -11
- package/src/shared/feishu-sdk/index.ts +0 -1
- package/src/shared/git-provider/adapters/gitea.adapter.ts +59 -22
- package/src/shared/git-provider/adapters/github.adapter.spec.ts +2 -1
- package/src/shared/git-provider/adapters/github.adapter.ts +200 -29
- package/src/shared/git-provider/adapters/gitlab.adapter.ts +78 -26
- package/src/shared/git-provider/git-provider.interface.ts +20 -1
- package/src/shared/git-provider/git-provider.service.ts +28 -6
- package/src/shared/git-provider/index.ts +0 -1
- package/src/shared/git-provider/types.ts +27 -0
- package/src/shared/git-sdk/git-sdk.service.ts +0 -2
- package/src/shared/git-sdk/index.ts +0 -1
- package/src/shared/llm-proxy/adapters/claude-code.adapter.spec.ts +15 -32
- package/src/shared/llm-proxy/adapters/claude-code.adapter.ts +5 -6
- package/src/shared/llm-proxy/adapters/open-code.adapter.ts +1 -3
- package/src/shared/llm-proxy/adapters/openai.adapter.spec.ts +7 -21
- package/src/shared/llm-proxy/adapters/openai.adapter.ts +1 -3
- package/src/shared/llm-proxy/index.ts +0 -1
- package/src/shared/llm-proxy/interfaces/config.interface.ts +8 -0
- package/src/shared/llm-proxy/llm-proxy.service.spec.ts +91 -68
- package/src/shared/llm-proxy/llm-proxy.service.ts +5 -8
- package/src/shared/mcp/index.ts +1 -33
- package/src/shared/output/index.ts +0 -1
- package/src/shared/output/output.service.ts +43 -13
- package/src/shared/rspack-config/rspack-config.ts +0 -6
- package/src/shared/storage/index.ts +0 -1
- package/src/shared/storage/storage.service.ts +16 -8
- package/src/shared/storage/types.ts +0 -44
- package/src/shared/verbose/index.ts +15 -0
- package/src/app.module.ts +0 -18
- package/src/config/config-reader.module.ts +0 -16
- package/src/shared/claude-setup/claude-setup.module.ts +0 -8
- package/src/shared/feishu-sdk/feishu-sdk.module.ts +0 -77
- package/src/shared/git-provider/git-provider.module.ts +0 -73
- package/src/shared/git-sdk/git-sdk.module.ts +0 -8
- package/src/shared/llm-proxy/llm-proxy.module.ts +0 -140
- package/src/shared/output/output.module.ts +0 -9
- package/src/shared/storage/storage.module.ts +0 -150
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
EditPullRequestOption,
|
|
20
20
|
User,
|
|
21
21
|
RepositoryContent,
|
|
22
|
+
ResolvedThread,
|
|
22
23
|
} from "./types";
|
|
23
24
|
|
|
24
25
|
/** PR 列表查询选项 */
|
|
@@ -164,7 +165,15 @@ export interface GitProvider {
|
|
|
164
165
|
): Promise<PullReview>;
|
|
165
166
|
/** 列出 PR 的所有 Reviews */
|
|
166
167
|
listPullReviews(owner: string, repo: string, index: number): Promise<PullReview[]>;
|
|
167
|
-
/**
|
|
168
|
+
/** 更新 PR Review 的内容 */
|
|
169
|
+
updatePullReview(
|
|
170
|
+
owner: string,
|
|
171
|
+
repo: string,
|
|
172
|
+
index: number,
|
|
173
|
+
reviewId: number,
|
|
174
|
+
body: string,
|
|
175
|
+
): Promise<PullReview>;
|
|
176
|
+
/** 删除 PR Review(仅支持 PENDING 状态) */
|
|
168
177
|
deletePullReview(owner: string, repo: string, index: number, reviewId: number): Promise<void>;
|
|
169
178
|
/** 获取 PR Review 的行级评论列表 */
|
|
170
179
|
listPullReviewComments(
|
|
@@ -173,10 +182,20 @@ export interface GitProvider {
|
|
|
173
182
|
index: number,
|
|
174
183
|
reviewId: number,
|
|
175
184
|
): Promise<PullReviewComment[]>;
|
|
185
|
+
/** 删除 PR Review 的单条行级评论 */
|
|
186
|
+
deletePullReviewComment(owner: string, repo: string, commentId: number): Promise<void>;
|
|
187
|
+
/** 获取 PR 中所有已解决的 review threads(含文件路径和行号) */
|
|
188
|
+
listResolvedThreads(owner: string, repo: string, index: number): Promise<ResolvedThread[]>;
|
|
176
189
|
|
|
177
190
|
// ============ Reaction 操作 ============
|
|
178
191
|
/** 获取 Issue/PR 评论的 reactions */
|
|
179
192
|
getIssueCommentReactions(owner: string, repo: string, commentId: number): Promise<Reaction[]>;
|
|
193
|
+
/** 获取 PR Review 行级评论的 reactions */
|
|
194
|
+
getPullReviewCommentReactions(
|
|
195
|
+
owner: string,
|
|
196
|
+
repo: string,
|
|
197
|
+
commentId: number,
|
|
198
|
+
): Promise<Reaction[]>;
|
|
180
199
|
/** 获取 Issue/PR 的 reactions */
|
|
181
200
|
getIssueReactions(owner: string, repo: string, index: number): Promise<Reaction[]>;
|
|
182
201
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Inject, Injectable } from "@nestjs/common";
|
|
2
1
|
import type {
|
|
3
2
|
GitProvider,
|
|
4
3
|
LockBranchOptions,
|
|
@@ -6,7 +5,6 @@ import type {
|
|
|
6
5
|
} from "./git-provider.interface";
|
|
7
6
|
import {
|
|
8
7
|
type GitProviderModuleOptions,
|
|
9
|
-
GIT_PROVIDER_MODULE_OPTIONS,
|
|
10
8
|
type BranchProtection,
|
|
11
9
|
type CreateBranchProtectionOption,
|
|
12
10
|
type EditBranchProtectionOption,
|
|
@@ -27,6 +25,7 @@ import {
|
|
|
27
25
|
type EditPullRequestOption,
|
|
28
26
|
type User,
|
|
29
27
|
type RepositoryContent,
|
|
28
|
+
type ResolvedThread,
|
|
30
29
|
} from "./types";
|
|
31
30
|
import { GiteaAdapter } from "./adapters/gitea.adapter";
|
|
32
31
|
import { GithubAdapter } from "./adapters/github.adapter";
|
|
@@ -36,13 +35,10 @@ import { GitlabAdapter } from "./adapters/gitlab.adapter";
|
|
|
36
35
|
* Git Provider 统一服务
|
|
37
36
|
* 根据配置的 provider 类型代理到对应的适配器实现
|
|
38
37
|
*/
|
|
39
|
-
@Injectable()
|
|
40
38
|
export class GitProviderService implements GitProvider {
|
|
41
39
|
protected readonly adapter: GitProvider;
|
|
42
40
|
|
|
43
|
-
constructor(
|
|
44
|
-
@Inject(GIT_PROVIDER_MODULE_OPTIONS) protected readonly options: GitProviderModuleOptions,
|
|
45
|
-
) {
|
|
41
|
+
constructor(protected readonly options: GitProviderModuleOptions) {
|
|
46
42
|
this.adapter = this.createAdapter(options);
|
|
47
43
|
}
|
|
48
44
|
|
|
@@ -265,6 +261,16 @@ export class GitProviderService implements GitProvider {
|
|
|
265
261
|
return this.adapter.listPullReviews(owner, repo, index);
|
|
266
262
|
}
|
|
267
263
|
|
|
264
|
+
async updatePullReview(
|
|
265
|
+
owner: string,
|
|
266
|
+
repo: string,
|
|
267
|
+
index: number,
|
|
268
|
+
reviewId: number,
|
|
269
|
+
body: string,
|
|
270
|
+
): Promise<PullReview> {
|
|
271
|
+
return this.adapter.updatePullReview(owner, repo, index, reviewId, body);
|
|
272
|
+
}
|
|
273
|
+
|
|
268
274
|
async deletePullReview(
|
|
269
275
|
owner: string,
|
|
270
276
|
repo: string,
|
|
@@ -283,6 +289,14 @@ export class GitProviderService implements GitProvider {
|
|
|
283
289
|
return this.adapter.listPullReviewComments(owner, repo, index, reviewId);
|
|
284
290
|
}
|
|
285
291
|
|
|
292
|
+
async deletePullReviewComment(owner: string, repo: string, commentId: number): Promise<void> {
|
|
293
|
+
return this.adapter.deletePullReviewComment(owner, repo, commentId);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async listResolvedThreads(owner: string, repo: string, index: number): Promise<ResolvedThread[]> {
|
|
297
|
+
return this.adapter.listResolvedThreads(owner, repo, index);
|
|
298
|
+
}
|
|
299
|
+
|
|
286
300
|
// ============ Reaction 操作 ============
|
|
287
301
|
|
|
288
302
|
async getIssueCommentReactions(
|
|
@@ -293,6 +307,14 @@ export class GitProviderService implements GitProvider {
|
|
|
293
307
|
return this.adapter.getIssueCommentReactions(owner, repo, commentId);
|
|
294
308
|
}
|
|
295
309
|
|
|
310
|
+
async getPullReviewCommentReactions(
|
|
311
|
+
owner: string,
|
|
312
|
+
repo: string,
|
|
313
|
+
commentId: number,
|
|
314
|
+
): Promise<Reaction[]> {
|
|
315
|
+
return this.adapter.getPullReviewCommentReactions(owner, repo, commentId);
|
|
316
|
+
}
|
|
317
|
+
|
|
296
318
|
async getIssueReactions(owner: string, repo: string, index: number): Promise<Reaction[]> {
|
|
297
319
|
return this.adapter.getIssueReactions(owner, repo, index);
|
|
298
320
|
}
|
|
@@ -318,6 +318,20 @@ export interface Issue {
|
|
|
318
318
|
/** PR Review 事件类型 */
|
|
319
319
|
export type ReviewStateType = "APPROVE" | "REQUEST_CHANGES" | "COMMENT" | "PENDING";
|
|
320
320
|
|
|
321
|
+
/** PR Review 事件常量 */
|
|
322
|
+
export const REVIEW_STATE = {
|
|
323
|
+
APPROVE: "APPROVE",
|
|
324
|
+
REQUEST_CHANGES: "REQUEST_CHANGES",
|
|
325
|
+
COMMENT: "COMMENT",
|
|
326
|
+
PENDING: "PENDING",
|
|
327
|
+
} as const;
|
|
328
|
+
|
|
329
|
+
/** PR Review 行级评论 Diff 方向 */
|
|
330
|
+
export const DIFF_SIDE = {
|
|
331
|
+
RIGHT: "RIGHT",
|
|
332
|
+
LEFT: "LEFT",
|
|
333
|
+
} as const;
|
|
334
|
+
|
|
321
335
|
/** PR Review 行级评论 */
|
|
322
336
|
export interface CreatePullReviewComment {
|
|
323
337
|
/** 文件路径 */
|
|
@@ -381,6 +395,19 @@ export interface PullReviewComment {
|
|
|
381
395
|
html_url?: string;
|
|
382
396
|
}
|
|
383
397
|
|
|
398
|
+
/** PR Review Thread 已解决信息 */
|
|
399
|
+
export interface ResolvedThread {
|
|
400
|
+
/** 文件路径 */
|
|
401
|
+
path?: string;
|
|
402
|
+
/** 行号(新文件中的行号) */
|
|
403
|
+
line?: number;
|
|
404
|
+
/** 解决者 */
|
|
405
|
+
resolvedBy?: {
|
|
406
|
+
id?: number;
|
|
407
|
+
login?: string;
|
|
408
|
+
} | null;
|
|
409
|
+
}
|
|
410
|
+
|
|
384
411
|
/** 用户信息 */
|
|
385
412
|
export interface User {
|
|
386
413
|
id?: number;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Injectable } from "@nestjs/common";
|
|
2
1
|
import { spawn, execSync } from "child_process";
|
|
3
2
|
import type { GitCommit, GitChangedFile, GitDiffFile, GitRunOptions } from "./git-sdk.types";
|
|
4
3
|
import { mapGitStatus, parseDiffText } from "./git-sdk-diff.utils";
|
|
5
4
|
|
|
6
|
-
@Injectable()
|
|
7
5
|
export class GitSdkService {
|
|
8
6
|
protected readonly defaultOptions: GitRunOptions = {
|
|
9
7
|
cwd: process.cwd(),
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import { vi, type
|
|
2
|
-
import { Test, TestingModule } from "@nestjs/testing";
|
|
1
|
+
import { vi, type Mock } from "vitest";
|
|
3
2
|
import { ClaudeCodeAdapter } from "./claude-code.adapter";
|
|
4
|
-
import { ClaudeSetupService } from "../../claude-setup";
|
|
5
3
|
import { LlmStreamEvent } from "../interfaces";
|
|
6
4
|
|
|
7
5
|
vi.mock("@anthropic-ai/claude-agent-sdk", () => ({
|
|
8
6
|
query: vi.fn(),
|
|
9
7
|
}));
|
|
10
8
|
|
|
9
|
+
vi.mock("../../claude-setup", () => ({
|
|
10
|
+
ClaudeSetupService: class MockClaudeSetupService {
|
|
11
|
+
configure = vi.fn().mockResolvedValue(undefined);
|
|
12
|
+
backup = vi.fn().mockResolvedValue(undefined);
|
|
13
|
+
restore = vi.fn().mockResolvedValue(undefined);
|
|
14
|
+
withTemporaryConfig = vi.fn();
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
11
18
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
12
19
|
|
|
13
20
|
describe("ClaudeAdapter", () => {
|
|
14
21
|
let adapter: ClaudeCodeAdapter;
|
|
15
|
-
let claudeSetupService: Mocked<ClaudeSetupService>;
|
|
16
22
|
|
|
17
23
|
const mockConfig = {
|
|
18
24
|
claudeCode: {
|
|
@@ -21,24 +27,9 @@ describe("ClaudeAdapter", () => {
|
|
|
21
27
|
},
|
|
22
28
|
};
|
|
23
29
|
|
|
24
|
-
beforeEach(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
backup: vi.fn().mockResolvedValue(undefined),
|
|
28
|
-
restore: vi.fn().mockResolvedValue(undefined),
|
|
29
|
-
withTemporaryConfig: vi.fn(),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const module: TestingModule = await Test.createTestingModule({
|
|
33
|
-
providers: [
|
|
34
|
-
ClaudeCodeAdapter,
|
|
35
|
-
{ provide: "LLM_PROXY_CONFIG", useValue: mockConfig },
|
|
36
|
-
{ provide: ClaudeSetupService, useValue: mockSetup },
|
|
37
|
-
],
|
|
38
|
-
}).compile();
|
|
39
|
-
|
|
40
|
-
adapter = module.get<ClaudeCodeAdapter>(ClaudeCodeAdapter);
|
|
41
|
-
claudeSetupService = module.get(ClaudeSetupService);
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
vi.clearAllMocks();
|
|
32
|
+
adapter = new ClaudeCodeAdapter(mockConfig as any);
|
|
42
33
|
});
|
|
43
34
|
|
|
44
35
|
it("should be defined", () => {
|
|
@@ -51,15 +42,8 @@ describe("ClaudeAdapter", () => {
|
|
|
51
42
|
expect(adapter.isConfigured()).toBe(true);
|
|
52
43
|
});
|
|
53
44
|
|
|
54
|
-
it("should return false if claude config is missing",
|
|
55
|
-
const
|
|
56
|
-
providers: [
|
|
57
|
-
ClaudeCodeAdapter,
|
|
58
|
-
{ provide: "LLM_PROXY_CONFIG", useValue: {} },
|
|
59
|
-
{ provide: ClaudeSetupService, useValue: { configure: vi.fn() } },
|
|
60
|
-
],
|
|
61
|
-
}).compile();
|
|
62
|
-
const unconfiguredAdapter = module.get<ClaudeCodeAdapter>(ClaudeCodeAdapter);
|
|
45
|
+
it("should return false if claude config is missing", () => {
|
|
46
|
+
const unconfiguredAdapter = new ClaudeCodeAdapter({} as any);
|
|
63
47
|
expect(unconfiguredAdapter.isConfigured()).toBe(false);
|
|
64
48
|
});
|
|
65
49
|
});
|
|
@@ -79,7 +63,6 @@ describe("ClaudeAdapter", () => {
|
|
|
79
63
|
events.push(event);
|
|
80
64
|
}
|
|
81
65
|
|
|
82
|
-
expect(claudeSetupService.configure).toHaveBeenCalled();
|
|
83
66
|
expect(query).toHaveBeenCalledWith(
|
|
84
67
|
expect.objectContaining({
|
|
85
68
|
prompt: "hello",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Injectable, Inject } from "@nestjs/common";
|
|
2
1
|
import { query, type SpawnOptions } from "@anthropic-ai/claude-agent-sdk";
|
|
3
2
|
import { spawn } from "child_process";
|
|
4
3
|
import type { LlmAdapter } from "./llm-adapter.interface";
|
|
@@ -12,14 +11,14 @@ import type {
|
|
|
12
11
|
import { ClaudeSetupService } from "../../claude-setup";
|
|
13
12
|
import { shouldLog } from "../../verbose";
|
|
14
13
|
|
|
15
|
-
@Injectable()
|
|
16
14
|
export class ClaudeCodeAdapter implements LlmAdapter {
|
|
17
15
|
readonly name = "claude-code";
|
|
16
|
+
private readonly claudeSetupService: ClaudeSetupService;
|
|
18
17
|
|
|
19
|
-
constructor(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
constructor(private readonly config: LlmProxyConfig) {
|
|
19
|
+
// 创建 ClaudeSetupService 实例,传入 LLM 配置
|
|
20
|
+
this.claudeSetupService = new ClaudeSetupService(config);
|
|
21
|
+
}
|
|
23
22
|
|
|
24
23
|
isConfigured(): boolean {
|
|
25
24
|
return !!this.config.claudeCode;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Injectable, Inject } from "@nestjs/common";
|
|
2
1
|
import { createOpencode } from "@opencode-ai/sdk";
|
|
3
2
|
import type { LlmAdapter } from "./llm-adapter.interface";
|
|
4
3
|
import type {
|
|
@@ -11,11 +10,10 @@ import type {
|
|
|
11
10
|
} from "../interfaces";
|
|
12
11
|
import { shouldLog } from "../../verbose";
|
|
13
12
|
|
|
14
|
-
@Injectable()
|
|
15
13
|
export class OpenCodeAdapter implements LlmAdapter {
|
|
16
14
|
readonly name = "open-code";
|
|
17
15
|
|
|
18
|
-
constructor(
|
|
16
|
+
constructor(private readonly config: LlmProxyConfig) {}
|
|
19
17
|
|
|
20
18
|
isConfigured(): boolean {
|
|
21
19
|
return !!this.config.openCode;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { vi, type Mock } from "vitest";
|
|
2
|
-
import { Test, TestingModule } from "@nestjs/testing";
|
|
3
2
|
import { OpenAIAdapter } from "./openai.adapter";
|
|
4
3
|
import OpenAI from "openai";
|
|
5
4
|
|
|
@@ -17,7 +16,8 @@ describe("OpenAIAdapter", () => {
|
|
|
17
16
|
},
|
|
18
17
|
};
|
|
19
18
|
|
|
20
|
-
beforeEach(
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
21
|
mockOpenAIInstance = {
|
|
22
22
|
chat: {
|
|
23
23
|
completions: {
|
|
@@ -28,12 +28,7 @@ describe("OpenAIAdapter", () => {
|
|
|
28
28
|
(OpenAI as unknown as Mock).mockImplementation(function () {
|
|
29
29
|
return mockOpenAIInstance;
|
|
30
30
|
});
|
|
31
|
-
|
|
32
|
-
const module: TestingModule = await Test.createTestingModule({
|
|
33
|
-
providers: [OpenAIAdapter, { provide: "LLM_PROXY_CONFIG", useValue: mockConfig }],
|
|
34
|
-
}).compile();
|
|
35
|
-
|
|
36
|
-
adapter = module.get<OpenAIAdapter>(OpenAIAdapter);
|
|
31
|
+
adapter = new OpenAIAdapter(mockConfig as any);
|
|
37
32
|
});
|
|
38
33
|
|
|
39
34
|
it("should be defined", () => {
|
|
@@ -46,11 +41,8 @@ describe("OpenAIAdapter", () => {
|
|
|
46
41
|
expect(adapter.isConfigured()).toBe(true);
|
|
47
42
|
});
|
|
48
43
|
|
|
49
|
-
it("should return false if openai config is missing",
|
|
50
|
-
const
|
|
51
|
-
providers: [OpenAIAdapter, { provide: "LLM_PROXY_CONFIG", useValue: {} }],
|
|
52
|
-
}).compile();
|
|
53
|
-
const unconfiguredAdapter = module.get<OpenAIAdapter>(OpenAIAdapter);
|
|
44
|
+
it("should return false if openai config is missing", () => {
|
|
45
|
+
const unconfiguredAdapter = new OpenAIAdapter({} as any);
|
|
54
46
|
expect(unconfiguredAdapter.isConfigured()).toBe(false);
|
|
55
47
|
});
|
|
56
48
|
});
|
|
@@ -156,10 +148,7 @@ describe("OpenAIAdapter", () => {
|
|
|
156
148
|
});
|
|
157
149
|
|
|
158
150
|
it("should yield error when openai not configured", async () => {
|
|
159
|
-
const
|
|
160
|
-
providers: [OpenAIAdapter, { provide: "LLM_PROXY_CONFIG", useValue: {} }],
|
|
161
|
-
}).compile();
|
|
162
|
-
const unconfigured = module.get<OpenAIAdapter>(OpenAIAdapter);
|
|
151
|
+
const unconfigured = new OpenAIAdapter({} as any);
|
|
163
152
|
const events: any[] = [];
|
|
164
153
|
for await (const event of unconfigured.chatStream([{ role: "user", content: "hi" }] as any)) {
|
|
165
154
|
events.push(event);
|
|
@@ -176,10 +165,7 @@ describe("OpenAIAdapter", () => {
|
|
|
176
165
|
});
|
|
177
166
|
|
|
178
167
|
it("should throw when openai not configured for chat", async () => {
|
|
179
|
-
const
|
|
180
|
-
providers: [OpenAIAdapter, { provide: "LLM_PROXY_CONFIG", useValue: {} }],
|
|
181
|
-
}).compile();
|
|
182
|
-
const unconfigured = module.get<OpenAIAdapter>(OpenAIAdapter);
|
|
168
|
+
const unconfigured = new OpenAIAdapter({} as any);
|
|
183
169
|
await expect(unconfigured.chat([{ role: "user", content: "hi" }] as any)).rejects.toThrow(
|
|
184
170
|
"未配置 openai",
|
|
185
171
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Injectable, Inject } from "@nestjs/common";
|
|
2
1
|
import OpenAI from "openai";
|
|
3
2
|
import type { LlmAdapter } from "./llm-adapter.interface";
|
|
4
3
|
import type {
|
|
@@ -10,13 +9,12 @@ import type {
|
|
|
10
9
|
} from "../interfaces";
|
|
11
10
|
import { shouldLog } from "../../verbose";
|
|
12
11
|
|
|
13
|
-
@Injectable()
|
|
14
12
|
export class OpenAIAdapter implements LlmAdapter {
|
|
15
13
|
readonly name = "openai";
|
|
16
14
|
|
|
17
15
|
private client: OpenAI | null = null;
|
|
18
16
|
|
|
19
|
-
constructor(
|
|
17
|
+
constructor(private readonly config: LlmProxyConfig) {}
|
|
20
18
|
|
|
21
19
|
isConfigured(): boolean {
|
|
22
20
|
return !!this.config.openai;
|
|
@@ -2,6 +2,7 @@ export interface ClaudeAdapterConfig {
|
|
|
2
2
|
model?: string;
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
authToken?: string;
|
|
5
|
+
hasCompletedOnboarding?: boolean;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
export interface OpenAIAdapterConfig {
|
|
@@ -22,11 +23,18 @@ export interface OpenCodeAdapterConfig {
|
|
|
22
23
|
|
|
23
24
|
export type LLMMode = "claude-code" | "openai" | "gemini" | "open-code";
|
|
24
25
|
|
|
26
|
+
export interface GeminiAdapterConfig {
|
|
27
|
+
model?: string;
|
|
28
|
+
baseUrl?: string;
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
export interface LlmProxyConfig {
|
|
26
33
|
defaultAdapter?: LLMMode;
|
|
27
34
|
claudeCode?: ClaudeAdapterConfig;
|
|
28
35
|
openai?: OpenAIAdapterConfig;
|
|
29
36
|
openCode?: OpenCodeAdapterConfig;
|
|
37
|
+
gemini?: GeminiAdapterConfig;
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
export const LLM_PROXY_CONFIG = Symbol("LLM_PROXY_CONFIG");
|