mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
feat: implement Claude and OpenAI conversation naming service
- Add Claude and OpenAI ConversationNamingService with API integration - Register Claude and OpenAI naming service in dependency injection - Add model and URL constants for naming
This commit is contained in:
parent
bf948a79b2
commit
6656f4c495
5 changed files with 120 additions and 8 deletions
|
|
@ -1,9 +1,53 @@
|
|||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
import type { IConversationNamingService } from "AIClasses/IConversationNamingService";
|
||||
import type AIAgentPlugin from "main";
|
||||
import { AIProviderURL, AIProviderModel } from "Enums/ApiProvider";
|
||||
import { Role } from "Enums/Role";
|
||||
import { NamePrompt } from "AIClasses/NamePrompt";
|
||||
|
||||
export class ClaudeConversationNamingService implements IConversationNamingService {
|
||||
|
||||
public async generateName(userPrompt: string, abortSignal?: AbortSignal): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
private readonly apiKey: string;
|
||||
|
||||
public constructor() {
|
||||
this.apiKey = Resolve<AIAgentPlugin>(Services.AIAgentPlugin).settings.apiKey;
|
||||
}
|
||||
|
||||
public async generateName(userPrompt: string, abortSignal?: AbortSignal): Promise<string> {
|
||||
|
||||
const requestBody = {
|
||||
model: AIProviderModel.ClaudeNamer,
|
||||
max_tokens: 50,
|
||||
system: NamePrompt,
|
||||
messages: [{
|
||||
role: Role.User,
|
||||
content: userPrompt
|
||||
}]
|
||||
};
|
||||
|
||||
const response = await fetch(AIProviderURL.ClaudeNamer, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-api-key': this.apiKey,
|
||||
'anthropic-version': '2023-06-01',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: abortSignal
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Claude API error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const generatedName = data.content?.[0]?.text;
|
||||
|
||||
if (!generatedName) {
|
||||
throw new Error("Failed to generate conversation name");
|
||||
}
|
||||
|
||||
return generatedName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,11 +7,11 @@ import { Role } from "Enums/Role";
|
|||
import { NamePrompt } from "AIClasses/NamePrompt";
|
||||
|
||||
export class GeminiConversationNamingService implements IConversationNamingService {
|
||||
|
||||
private readonly apiKey: string;
|
||||
private readonly plugin: AIAgentPlugin = Resolve<AIAgentPlugin>(Services.AIAgentPlugin);
|
||||
|
||||
public constructor() {
|
||||
this.apiKey = this.plugin.settings.apiKey;
|
||||
this.apiKey = Resolve<AIAgentPlugin>(Services.AIAgentPlugin).settings.apiKey;
|
||||
}
|
||||
|
||||
public async generateName(userPrompt: string, abortSignal?: AbortSignal): Promise<string> {
|
||||
|
|
|
|||
57
AIClasses/OpenAI/OpenAIConversationNamingService.ts
Normal file
57
AIClasses/OpenAI/OpenAIConversationNamingService.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
import type { IConversationNamingService } from "AIClasses/IConversationNamingService";
|
||||
import type AIAgentPlugin from "main";
|
||||
import { AIProviderURL, AIProviderModel } from "Enums/ApiProvider";
|
||||
import { Role } from "Enums/Role";
|
||||
import { NamePrompt } from "AIClasses/NamePrompt";
|
||||
|
||||
export class OpenAIConversationNamingService implements IConversationNamingService {
|
||||
|
||||
private readonly apiKey: string;
|
||||
|
||||
public constructor() {
|
||||
this.apiKey = Resolve<AIAgentPlugin>(Services.AIAgentPlugin).settings.apiKey;
|
||||
}
|
||||
|
||||
public async generateName(userPrompt: string, abortSignal?: AbortSignal): Promise<string> {
|
||||
|
||||
const requestBody = {
|
||||
model: AIProviderModel.OpenAINamer,
|
||||
max_tokens: 50,
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: NamePrompt
|
||||
},
|
||||
{
|
||||
role: Role.User,
|
||||
content: userPrompt
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const response = await fetch(AIProviderURL.OpenAINamer, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: abortSignal
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const generatedName = data.choices?.[0]?.message?.content;
|
||||
|
||||
if (!generatedName) {
|
||||
throw new Error("Failed to generate conversation name");
|
||||
}
|
||||
|
||||
return generatedName;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,20 @@ export enum AIProvider {
|
|||
|
||||
export enum AIProviderModel {
|
||||
Claude = "claude-sonnet-4-5",
|
||||
ClaudeNamer = "claude-haiku-4-5",
|
||||
|
||||
Gemini = "gemini-2.5-flash",
|
||||
OpenAI = ""
|
||||
GeminiNamer = "gemini-2.5-flash",
|
||||
|
||||
OpenAI = "gpt-5",
|
||||
OpenAINamer = "gpt-5-nano"
|
||||
}
|
||||
|
||||
export enum AIProviderURL {
|
||||
Gemini = `https://generativelanguage.googleapis.com/v1beta/models/${AIProviderModel.Gemini}:streamGenerateContent?key=API_KEY&alt=sse`,
|
||||
GeminiNamer = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=API_KEY"
|
||||
ClaudeNamer = "https://api.anthropic.com/v1/messages",
|
||||
|
||||
Gemini = `https://generativelanguage.googleapis.com/v1beta/models/${AIProviderModel.GeminiNamer}:streamGenerateContent?key=API_KEY&alt=sse`,
|
||||
GeminiNamer = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=API_KEY",
|
||||
|
||||
OpenAINamer = "https://api.openai.com/v1/chat/completions"
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import { GeminiTokenService } from "AIClasses/Gemini/GeminiTokenService";
|
|||
import { StatusBarService } from "./StatusBarService";
|
||||
import { ClaudeTokenService } from "AIClasses/Claude/ClaudeTokenService";
|
||||
import { OpenAITokenService } from "AIClasses/OpenAI/OpenAITokenService";
|
||||
import { ClaudeConversationNamingService } from "AIClasses/Claude/ClaudeConversationNamingService";
|
||||
|
||||
export function RegisterDependencies(plugin: AIAgentPlugin) {
|
||||
RegisterSingleton<AIAgentPlugin>(Services.AIAgentPlugin, plugin);
|
||||
|
|
@ -50,6 +51,7 @@ export function RegisterDependencies(plugin: AIAgentPlugin) {
|
|||
export function RegisterAiProvider(plugin: AIAgentPlugin) {
|
||||
if (plugin.settings.apiProvider == AIProvider.Claude) {
|
||||
RegisterSingleton<ITokenService>(Services.ITokenService, new ClaudeTokenService());
|
||||
RegisterSingleton<IConversationNamingService>(Services.IConversationNamingService, new ClaudeConversationNamingService());
|
||||
}
|
||||
else if (plugin.settings.apiProvider == AIProvider.Gemini) {
|
||||
RegisterSingleton<IAIClass>(Services.IAIClass, new Gemini());
|
||||
|
|
|
|||
Loading…
Reference in a new issue