Skip to content

Commit

Permalink
✨ feat(weeklyReport): 添加生成周报的功能
Browse files Browse the repository at this point in the history
- 在 AIProvider 接口中添加 generateWeeklyReport 方法
- 实现 generateWeeklyReport 方法在 OpenAI、Ollama 和 VSCode 提供商中
- 更新 GenerateWeeklyReportCommand 命令,集成生成周报功能
- 修改周报面板,展示生成的周报内容
- 调整相关文件以支持周报生成
  • Loading branch information
littleCareless committed Dec 12, 2024
1 parent b557c7a commit 04e999e
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 50 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
},
{
"command": "dish-ai-commit.generateWeeklyReport",
"when": ""
"when": "true"
}
]
}
Expand Down
4 changes: 2 additions & 2 deletions src/ai/AIProviderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class AIProviderFactory {
let provider = this.providers.get(providerType);
console.log("AIProvider", AIProvider);
console.log("providerType", providerType.toLowerCase());
console.log("AIProvider.VSCODE", AIProvider.VSCODE);
console.log("AIProvider.VSCODE", AIProvider.ZHIPU);
if (!provider) {
switch (providerType.toLowerCase()) {
case AIProvider.OPENAI:
Expand All @@ -46,7 +46,7 @@ export class AIProviderFactory {
case AIProvider.VS_CODE_PROVIDED:
provider = new VSCodeProvider();
break;
case AIProvider.ZHIPU:
case AIProvider.ZHIPUAI:
provider = new ZhipuAIProvider();
break;
case AIProvider.DASHSCOPE:
Expand Down
37 changes: 37 additions & 0 deletions src/ai/providers/BaseOpenAIProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import OpenAI from "openai";
import { ChatCompletionMessageParam } from "openai/resources";
import { AIProvider, AIRequestParams, AIResponse, AIModel } from "../types";
import { generateWithRetry, getSystemPrompt } from "../utils/generateHelper";
import { LocalizationManager } from "../../utils/LocalizationManager";

export interface OpenAIProviderConfig {
apiKey: string;
Expand Down Expand Up @@ -83,6 +84,42 @@ export abstract class BaseOpenAIProvider implements AIProvider {
);
}

async generateWeeklyReport(commits: string[]): 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,
});

return {
content: completion.choices[0]?.message?.content || "",
usage: {
promptTokens: completion.usage?.prompt_tokens,
completionTokens: completion.usage?.completion_tokens,
totalTokens: completion.usage?.total_tokens,
},
};
} catch (error) {
throw new Error(
LocalizationManager.getInstance().format(
"weeklyReport.generation.failed",
error instanceof Error ? error.message : String(error)
)
);
}
}

async getModels(): Promise<AIModel[]> {
return Promise.resolve(this.config.models);
}
Expand Down
34 changes: 34 additions & 0 deletions src/ai/providers/OllamaProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,40 @@ export class OllamaProvider implements AIProvider {
);
}

async generateWeeklyReport(commits: string[]): Promise<AIResponse> {
const model = this.configManager.getConfig("BASE_MODEL");

const response = await this.ollama.chat({
model: (model as any).id,
messages: [
{
role: "system",
content: "请根据以下commit生成一份周报:",
},
{
role: "user",
content: commits.join("\n"),
},
],
stream: false,
});

let content = "";
try {
const jsonContent = JSON.parse(response.message.content);
content = jsonContent.response || response.message.content;
} catch {
content = response.message.content;
}

return {
content,
usage: {
totalTokens: response.total_duration,
},
};
}

async isAvailable(): Promise<boolean> {
try {
await this.ollama.list();
Expand Down
36 changes: 36 additions & 0 deletions src/ai/providers/VscodeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,42 @@ export class VSCodeProvider implements AIProvider {
}
}

async generateWeeklyReport(commits: string[]): Promise<AIResponse> {
try {
const models = await vscode.lm.selectChatModels();
if (!models || models.length === 0) {
throw new Error(
LocalizationManager.getInstance().getMessage(
"vscode.no.models.available"
)
);
}

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

const response = await chatModel.sendRequest(messages);

let result = "";
for await (const fragment of response.text) {
result += fragment;
}

return { content: result.trim() };
} catch (error) {
throw new Error(
LocalizationManager.getInstance().format(
"weeklyReport.generation.failed",
error instanceof Error ? error.message : String(error)
)
);
}
}

async refreshModels(): Promise<any> {
// VSCode的模型是动态的,不需要刷新
return Promise.resolve();
Expand Down
1 change: 1 addition & 0 deletions src/ai/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export interface AIModel<

export interface AIProvider {
generateResponse(params: AIRequestParams): Promise<AIResponse>;
generateWeeklyReport(commits: string[]): Promise<AIResponse>;
isAvailable(): Promise<boolean>;
refreshModels(): Promise<string[]>;
getModels(): Promise<AIModel[]>; // 更新返回类型
Expand Down
2 changes: 0 additions & 2 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export class CommandManager implements vscode.Disposable {
const generateCommand = new GenerateCommitCommand(this.context);
const selectModelCommand = new SelectModelCommand(this.context);
const weeklyReportCommand = new GenerateWeeklyReportCommand(this.context);
console.log("COMMANDS.MODEL.SHOW", COMMANDS.MODEL.SHOW);

this.disposables.push(
vscode.commands.registerCommand(
Expand All @@ -37,7 +36,6 @@ export class CommandManager implements vscode.Disposable {
try {
await selectModelCommand.execute();
} catch (error) {
console.log("error", error);
NotificationHandler.error(
"command.select.model.failed",
error instanceof Error ? error.message : String(error)
Expand Down
76 changes: 74 additions & 2 deletions src/commands/GenerateWeeklyReportCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { ProgressHandler } from "../utils/ProgressHandler";
import { LocalizationManager } from "../utils/LocalizationManager";
import { WeeklyReportPanel } from "../webview/WeeklyReportPanel";
import { SCMFactory } from "../scm/SCMProvider";
import { AIProviderFactory } from "../ai/AIProviderFactory";
import { exec } from "child_process";
import { ConfigurationManager } from "../config/ConfigurationManager";

export class GenerateWeeklyReportCommand extends BaseCommand {
async validateConfig(): Promise<boolean> {
Expand All @@ -19,16 +22,63 @@ export class GenerateWeeklyReportCommand extends BaseCommand {
return true;
}

async getPeriod(): Promise<string | undefined> {
const options = ["本周", "上一周", "上两周"];
const selection = await vscode.window.showQuickPick(options, {
placeHolder: "选择一个时间段",
});

switch (selection) {
case "本周":
return "1 week ago";
case "上一周":
return "2 weeks ago";
case "上两周":
return "3 weeks ago";
default:
return undefined;
}
}

async execute(): Promise<void> {
try {
if (!(await this.validateConfig())) {
const period = await this.getPeriod();
if (!period) {
return;
}

await ProgressHandler.withProgress(
LocalizationManager.getInstance().getMessage("weeklyReport.generating"),
async () => {
WeeklyReportPanel.createOrShow(this.context.extensionUri);
const scmProvider = await SCMFactory.detectSCM();
if (!scmProvider) {
await NotificationHandler.error(
LocalizationManager.getInstance().getMessage("scm.not.detected")
);
return;
}

const config = ConfigurationManager.getInstance();
const configuration = config.getConfiguration();

// 检查是否已配置 AI 提供商和模型
let provider = configuration.base.provider;
let model = configuration.base.model;

const commits = await this.getCommits(period);
const aiProvider = AIProviderFactory.getProvider("ZHIPUAI");
const response = await aiProvider.generateWeeklyReport(commits);

if (response?.content) {
vscode.window.showInformationMessage("周报生成成功");
WeeklyReportPanel.createOrShow(this.context.extensionUri);
WeeklyReportPanel.currentPanel?._panel.webview.postMessage({
command: "report",
data: response.content,
});
} else {
vscode.window.showErrorMessage("周报生成失败");
}
}
);
} catch (error) {
Expand All @@ -42,4 +92,26 @@ export class GenerateWeeklyReportCommand extends BaseCommand {
}
}
}

private async getCommits(period: string): Promise<string[]> {
return new Promise((resolve, reject) => {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
return reject("没有打开的工作区");
}

const command = `git log --since="${period}" --pretty=format:"%h - %an, %ar : %s"`;
exec(
command,
{ cwd: workspaceFolders[0].uri.fsPath },
(error, stdout, stderr) => {
if (error) {
reject(`获取commit历史记录失败: ${stderr}`);
} else {
resolve(stdout.split("\n"));
}
}
);
});
}
}
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const COMMANDS = {
SHOW: packageJson.contributes.commands[1].command,
},
WEEKLY_REPORT: {
GENERATE: 'dish-ai-commit.generateWeeklyReport'
}
GENERATE: packageJson.contributes.commands[2].command,
},
} as const;

// 添加类型导出
Expand Down
9 changes: 4 additions & 5 deletions src/services/weeklyReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class WeeklyReportService {

constructor() {}

async generate(): Promise<WorkItem[]> {
async generate(commits: string[]): Promise<WorkItem[]> {
const scmProvider = await SCMFactory.detectSCM();
if (!scmProvider) {
throw new Error("No SCM provider detected");
Expand All @@ -30,8 +30,7 @@ export class WeeklyReportService {
throw new Error("Unable to detect author information");
}

const repositories = await this.findRepositories();
await this.collectLogs(repositories, author);
this.allLogs = commits;
return this.processLogs();
}

Expand Down Expand Up @@ -93,7 +92,7 @@ export class WeeklyReportService {
private getLastWeekDates(): { start: Date; end: Date } {
const today = new Date();
const currentDay = today.getDay();

// 计算上周一的日期
const lastMonday = new Date(today);
lastMonday.setDate(today.getDate() - currentDay - 7 + 1);
Expand All @@ -111,7 +110,7 @@ export class WeeklyReportService {
const { start, end } = this.getLastWeekDates();
const startDate = start.toISOString();
const endDate = end.toISOString();

const command = `git log --after="${startDate}" --before="${endDate}" --author="${author}" --pretty=format:"%s"`;
try {
const { stdout } = await execAsync(command, { cwd: repoPath });
Expand Down
Loading

0 comments on commit 04e999e

Please sign in to comment.