Skip to content

Commit

Permalink
✨ feat(周报): 添加周报生成功能配置和国际化支持
Browse files Browse the repository at this point in the history
- 新增系统提示词配置选项,支持自定义周报生成
- 扩展服务接口,支持通过不同AI模型生成周报
- 添加本地化文案,实现周报功能的多语言支持
- 重构配置管理器,优化配置项变更处理机制
- 优化界面组件,统一国际化文案标识
  • Loading branch information
littleCareless committed Dec 13, 2024
1 parent cd0f05e commit 7471d2c
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 162 deletions.
76 changes: 48 additions & 28 deletions i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"openai.apiKey.missing": "OpenAI API Key is not configured. Would you like to configure it now?",
"ollama.baseUrl.missing": "Ollama Base URL is not configured. Would you like to configure it now?",
"diff.simplification.warning": "Diff simplification is enabled. This may affect the accuracy of AI-generated commit messages. For more accurate commit messages, consider disabling this feature.",
"diff.simplification.warning": "Diff simplification is enabled. This may affect the accuracy of AI-generated commit messages. For more accurate results, consider disabling this feature.",
"command.execution.failed": "Command execution failed",
"scm.not.detected": "No supported version control system detected",
"no.changes": "No changes to commit",
"diff.too.long": "Changes content is too long, exceeding model limit. Please reduce the number of selected files or content length.\nCurrent length: {0} characters\nMaximum limit: {1} characters",
"diff.too.long": "Changes are too long and exceed the model's maximum limit. Please reduce the number of selected files or content length.\nCurrent length: {0} characters\nMaximum limit: {1} characters",
"commit.message.generated": "Commit message has been filled in {0} commit box (generated by {1} - {2})",
"commit.message.write.failed": "Failed to write commit message: {0}",
"commit.message.copied": "Commit message has been copied to clipboard",
"commit.message.copy.failed": "Failed to copy commit message: {0}",
"commit.message.manual.copy": "Commit message generated, please copy it manually to the commit box",
"commit.message.manual.copy": "Commit message has been generated, please manually copy to commit box",
"generate.commit.failed": "Failed to generate commit message: {0}",
"get.models.failed": "Failed to get model list",
"openai.config.required": "OpenAI API configuration is required to use this feature. Would you like to configure it now?",
"openai.baseUrl.prompt": "Enter OpenAI API URL",
"openai.baseUrl.placeholder": "e.g., https://api.openai.com/v1",
"openai.apiKey.prompt": "Enter OpenAI API Key",
"openai.baseUrl.prompt": "Please enter OpenAI API URL",
"openai.baseUrl.placeholder": "Example: https://api.openai.com/v1",
"openai.apiKey.prompt": "Please enter OpenAI API Key",
"ai.model.picker.title": "Select AI Model",
"ai.model.picker.placeholder": "Select AI model for generating commit messages",
"model.update.success": "AI model settings updated to: {0} - {1}",
Expand All @@ -25,44 +25,64 @@
"model.picker.placeholder": "Select AI model for generating commit messages",
"progress.generating.commit": "Generating {0} commit message...",
"progress.analyzing.changes": "Analyzing changes...",
"progress.generation.complete": "Generation completed",
"ollama.models.updated": "Ollama models list updated",
"ollama.models.fetch.failed": "Failed to fetch Ollama models",
"progress.generation.complete": "Generation complete",
"ollama.models.updated": "Ollama model list updated",
"ollama.models.fetch.failed": "Failed to fetch Ollama model list",
"ollama.api.call.failed": "Ollama API call failed",
"ollama.api.request.failed": "Ollama API request failed: {0}",
"localization.manager.not.initialized": "LocalizationManager not initialized",
"openai.config.invalid": "Invalid OpenAI configuration",
"openai.models.update.success": "OpenAI models list updated",
"openai.models.fetch.failed": "Failed to fetch OpenAI models",
"provider.not.available": "AI Provider {0} is not available",
"unexpected.error": "An unexpected error occurred: {0}",
"localization.manager.not.initialized": "Localization manager not initialized",
"openai.config.invalid": "OpenAI configuration invalid",
"openai.models.update.success": "OpenAI model list updated",
"openai.models.fetch.failed": "Failed to fetch OpenAI model list",
"provider.not.available": "AI provider {0} is not available",
"provider.type.unknown": "Unknown AI provider type: {0}",
"unexpected.error": "Unexpected error occurred: {0}",
"vscode.no.models.available": "No VSCode language models available",
"vscode.generation.failed": "VSCode AI generation failed: {0}",
"vscode.models.fetch.failed": "Failed to fetch VSCode models",
"provider.type.unknown": "Unknown AI provider type: {0}",
"vscode.models.fetch.failed": "Failed to fetch VSCode model list",
"button.yes": "Yes",
"button.no": "No",
"workspace.not.found": "No workspace folder found",
"workspace.not.found": "Workspace folder not found",
"git.diff.error": "Git diff error: {0}",
"diff.noChanges": "No changes found",
"diff.noChanges": "No changes detected",
"git.diff.failed": "Git diff failed: {0}",
"git.repository.not.found": "No Git repository found",
"git.repository.not.found": "Git repository not found",
"diff.noChangesFound": "No changes found",
"svn.commit.failed": "SVN commit failed: {0}",
"svn.no.changes": "No changes to commit in SVN",
"svn.repository.not.found": "No SVN repository found",
"openai.input.truncated": "Input content has been truncated, which may affect the quality of the generated result",
"svn.repository.not.found": "SVN repository not found",
"openai.input.truncated": "Input content too long and has been truncated, this may affect the quality of generated results",
"openai.generation.failed": "OpenAI generation failed: {0}",
"ollama.input.truncated": "Input content has been truncated, which may affect the quality of the generated result",
"ollama.input.truncated": "Input content too long and has been truncated, this may affect the quality of generated results",
"ollama.generation.failed": "Ollama generation failed: {0}",
"openai.models.empty": "No OpenAI models available",
"openai.models.error": "Failed to fetch OpenAI models list",
"openai.models.error": "Failed to get OpenAI model list",
"model.not.found": "Selected model not found",
"no.commit.message.generated": "No commit message was generated",
"input.truncated": "Input content exceeds maximum character limit and has been truncated",
"no.commit.message.generated": "No commit message generated",
"input.truncated": "Input content exceeds maximum character limit and has been truncated, this may affect the quality of generated results",
"extension.activation.failed": "Extension activation failed: {0}",
"command.register.failed": "Failed to register commands: {0}",
"command.register.failed": "Command registration failed: {0}",
"command.generate.failed": "Failed to generate commit message: {0}",
"command.select.model.failed": "Failed to select model: {0}",
"ai.model.loading": "Loading AI models list..."
"ai.model.loading": "Loading AI model list...",
"weeklyReport.generating": "Generating weekly report...",
"weeklyReport.empty.response": "AI generated content is empty",
"weeklyReport.generation.success": "Weekly report generated successfully",
"weeklyReport.generation.failed": "Failed to generate weekly report: {0}",
"weeklyReport.copy.success": "Content copied to clipboard",
"weeklyReport.copy.failed": "Copy failed: {0}",
"author.svn.not.found": "Unable to get SVN author information",
"author.manual.input.prompt": "Unable to automatically get username, please enter manually",
"author.manual.input.placeholder": "Please enter username",
"weeklyReport.title": "Weekly Report Generator",
"weeklyReport.period.current": "This Week",
"weeklyReport.period.lastWeek": "Last Week",
"weeklyReport.period.twoWeeksAgo": "Two Weeks Ago",
"weeklyReport.generate.button": "Generate Report",
"editor.format.bold": "Bold",
"editor.format.italic": "Italic",
"editor.format.underline": "Underline",
"editor.format.orderedList": "Ordered List",
"editor.format.unorderedList": "Unordered List",
"editor.copy": "Copy Content"
}
13 changes: 12 additions & 1 deletion i18n/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,16 @@
"weeklyReport.copy.failed": "复制失败: {0}",
"author.svn.not.found": "无法获取 SVN 作者信息",
"author.manual.input.prompt": "无法自动获取用户名,请手动输入",
"author.manual.input.placeholder": "请输入用户名"
"author.manual.input.placeholder": "请输入用户名",
"weeklyReport.title": "周报生成器",
"weeklyReport.period.current": "本周",
"weeklyReport.period.lastWeek": "上一周",
"weeklyReport.period.twoWeeksAgo": "上两周",
"weeklyReport.generate.button": "生成周报",
"editor.format.bold": "粗体",
"editor.format.italic": "斜体",
"editor.format.underline": "下划线",
"editor.format.orderedList": "有序列表",
"editor.format.unorderedList": "无序列表",
"editor.copy": "复制内容"
}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@
"type": "boolean",
"default": true,
"description": "在提交信息中使用 emoji"
},
"dish-ai-commit.features.weeklyReport.systemPrompt": {
"type": "string",
"default": "",
"description": "自定义周报生成的系统提示词"
}
}
},
Expand Down
40 changes: 21 additions & 19 deletions src/ai/providers/BaseOpenAIProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChatCompletionMessageParam } from "openai/resources";
import { AIProvider, AIRequestParams, AIResponse, AIModel } from "../types";
import { generateWithRetry, getSystemPrompt } from "../utils/generateHelper";
import { LocalizationManager } from "../../utils/LocalizationManager";
import { getWeeklyReportPrompt } from "../../prompt/weeklyReport";

export interface OpenAIProviderConfig {
apiKey: string;
Expand Down Expand Up @@ -84,30 +85,31 @@ export abstract class BaseOpenAIProvider implements AIProvider {
);
}

async generateWeeklyReport(commits: string[]): Promise<AIResponse> {
async generateWeeklyReport(
commits: string[],
model?: AIModel
): Promise<AIResponse> {
try {
const messages: ChatCompletionMessageParam[] = [
{
role: "system",
content: "请根据以下commit生成一份周报:",
},
{
role: "user",
content: commits.join("\n"),
},
];

const completion = await this.openai.chat.completions.create({
model: this.config.defaultModel || "gpt-3.5-turbo",
messages,
const response = await this.openai.chat.completions.create({
model: model?.id || this.config.defaultModel || "gpt-3.5-turbo",
messages: [
{
role: "system",
content: getWeeklyReportPrompt(),
},
{
role: "user",
content: commits.join("\n"),
},
],
});

return {
content: completion.choices[0]?.message?.content || "",
content: response.choices[0]?.message?.content || "",
usage: {
promptTokens: completion.usage?.prompt_tokens,
completionTokens: completion.usage?.completion_tokens,
totalTokens: completion.usage?.total_tokens,
promptTokens: response.usage?.prompt_tokens,
completionTokens: response.usage?.completion_tokens,
totalTokens: response.usage?.total_tokens,
},
};
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/ai/providers/DashScopeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class DashScopeProvider extends BaseOpenAIProvider {
constructor() {
const configManager = ConfigurationManager.getInstance();
super({
apiKey: configManager.getConfig("PROVIDERS_DASHSCOPE_APIKEY", false),
apiKey: configManager.getConfig("PROVIDERS_DASHSCOPE_APIKEY"),
baseURL: "https://api.dashscope.com/v1/services/chat/completions",
providerId: "dashscope",
providerName: "DashScope",
Expand Down
2 changes: 1 addition & 1 deletion src/ai/providers/DoubaoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class DoubaoProvider extends BaseOpenAIProvider {
constructor() {
const configManager = ConfigurationManager.getInstance();
super({
apiKey: configManager.getConfig("PROVIDERS_DOUBAO_APIKEY", false),
apiKey: configManager.getConfig("PROVIDERS_DOUBAO_APIKEY"),
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
providerId: "doubao",
providerName: "豆包 AI",
Expand Down
2 changes: 1 addition & 1 deletion src/ai/providers/GeminiAIProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class GeminiAIProvider extends BaseOpenAIProvider {
constructor() {
const configManager = ConfigurationManager.getInstance();
super({
apiKey: configManager.getConfig("PROVIDERS_GEMINI_APIKEY", false),
apiKey: configManager.getConfig("PROVIDERS_GEMINI_APIKEY"),
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
providerId: "gemini",
providerName: "Gemini AI",
Expand Down
13 changes: 9 additions & 4 deletions src/ai/providers/OllamaProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ConfigurationManager } from "../../config/ConfigurationManager";
import { NotificationHandler } from "../../utils/NotificationHandler";
import { LocalizationManager } from "../../utils/LocalizationManager";
import { generateWithRetry, getSystemPrompt } from "../utils/generateHelper";
import { getWeeklyReportPrompt } from "../../prompt/weeklyReport";

export class OllamaProvider implements AIProvider {
private ollama: Ollama;
Expand Down Expand Up @@ -96,15 +97,19 @@ export class OllamaProvider implements AIProvider {
);
}

async generateWeeklyReport(commits: string[]): Promise<AIResponse> {
const model = this.configManager.getConfig("BASE_MODEL");
async generateWeeklyReport(
commits: string[],
model?: AIModel
): Promise<AIResponse> {
const modelId =
model?.id || (this.configManager.getConfig("BASE_MODEL") as any).id;

const response = await this.ollama.chat({
model: (model as any).id,
model: modelId,
messages: [
{
role: "system",
content: "请根据以下commit生成一份周报:",
content: getWeeklyReportPrompt(),
},
{
role: "user",
Expand Down
6 changes: 3 additions & 3 deletions src/ai/providers/OpenAIProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ export class OpenAIProvider extends BaseOpenAIProvider {
constructor() {
const configManager = ConfigurationManager.getInstance();
super({
apiKey: configManager.getConfig("PROVIDERS_OPENAI_APIKEY", false),
baseURL: configManager.getConfig("PROVIDERS_OPENAI_BASEURL", false),
apiVersion: configManager.getConfig("BASE_MODEL", false),
apiKey: configManager.getConfig("PROVIDERS_OPENAI_APIKEY"),
baseURL: configManager.getConfig("PROVIDERS_OPENAI_BASEURL"),
apiVersion: configManager.getConfig("BASE_MODEL"),
providerId: "openai",
providerName: "OpenAI",
models: models,
Expand Down
16 changes: 11 additions & 5 deletions src/ai/providers/VscodeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { generateCommitMessageSystemPrompt } from "../../prompt/prompt";
import { LocalizationManager } from "../../utils/LocalizationManager";
import { getSystemPrompt } from "../utils/generateHelper";
import { getWeeklyReportPrompt } from "../../prompt/weeklyReport";

interface DiffBlock {
header: string;
Expand Down Expand Up @@ -107,7 +108,10 @@ export class VSCodeProvider implements AIProvider {
}
}

async generateWeeklyReport(commits: string[]): Promise<AIResponse> {
async generateWeeklyReport(
commits: string[],
model?: AIModel
): Promise<AIResponse> {
try {
const models = await vscode.lm.selectChatModels();
if (!models || models.length === 0) {
Expand All @@ -118,11 +122,13 @@ export class VSCodeProvider implements AIProvider {
);
}

const chatModel = models[0];
const chatModel = model
? models.find((m) => m.id === model.id) || models[0]
: models[0];

const messages = [
vscode.LanguageModelChatMessage.User(
`请根据以下commit生成一份周报:\n${commits.join("\n")}`
),
vscode.LanguageModelChatMessage.User(getWeeklyReportPrompt()),
vscode.LanguageModelChatMessage.User(commits.join("\n")),
];

const response = await chatModel.sendRequest(messages);
Expand Down
2 changes: 1 addition & 1 deletion src/ai/providers/ZhipuAIProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class ZhipuAIProvider extends BaseOpenAIProvider {
constructor() {
const configManager = ConfigurationManager.getInstance();
super({
apiKey: configManager.getConfig("PROVIDERS_ZHIPUAI_APIKEY", false),
apiKey: configManager.getConfig("PROVIDERS_ZHIPUAI_APIKEY"),
baseURL: "https://open.bigmodel.cn/api/paas/v4/",
providerId: "zhipu",
providerName: "zhipu",
Expand Down
2 changes: 1 addition & 1 deletion src/ai/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export interface AIModel<

export interface AIProvider {
generateResponse(params: AIRequestParams): Promise<AIResponse>;
generateWeeklyReport(commits: string[]): Promise<AIResponse>;
generateWeeklyReport(commits: string[], model?: AIModel): Promise<AIResponse>;
isAvailable(): Promise<boolean>;
refreshModels(): Promise<string[]>;
getModels(): Promise<AIModel[]>; // 更新返回类型
Expand Down
30 changes: 30 additions & 0 deletions src/config/ConfigSchema.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ConfigurationChangeEvent } from "vscode";

export const CONFIG_SCHEMA = {
base: {
// 基础配置
Expand Down Expand Up @@ -340,3 +342,31 @@ export function generateConfiguration(
traverse(schema as unknown as ConfigObject);
return result;
}

// 添加配置路径生成函数
export function getAllConfigPaths(schema: typeof CONFIG_SCHEMA): string[] {
const paths: string[] = [];

function traverse(obj: ConfigObject, currentPath: string = ""): void {
for (const [key, value] of Object.entries(obj)) {
const newPath = currentPath ? `${currentPath}.${key}` : key;
if (isConfigValue(value)) {
paths.push(newPath);
} else {
traverse(value as ConfigObject, newPath);
}
}
}

traverse(schema as unknown as ConfigObject);
return paths;
}

// 获取特定类别的配置路径
export function getCategoryConfigPaths(
schema: typeof CONFIG_SCHEMA,
category: keyof typeof CONFIG_SCHEMA
): string[] {
return getAllConfigPaths(schema)
.filter(path => path.startsWith(category));
}
Loading

0 comments on commit 7471d2c

Please sign in to comment.