mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Add claude 3 and proxy server (#329)
This commit is contained in:
parent
b16b677abc
commit
b119349dd9
11 changed files with 289 additions and 64 deletions
37
package-lock.json
generated
37
package-lock.json
generated
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"@huggingface/inference": "^2.6.4",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@langchain/anthropic": "^0.1.1",
|
||||
"@langchain/cohere": "^0.0.2",
|
||||
"@langchain/community": "^0.0.16",
|
||||
"@langchain/google-genai": "^0.0.7",
|
||||
|
|
@ -1863,6 +1864,42 @@
|
|||
"node": ">= 14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@langchain/anthropic": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-0.1.1.tgz",
|
||||
"integrity": "sha512-xt66bysyOp+Y3gWog/j6kTwi8GkLnEp7oacF+Z124mKZAO0+lKMxG4xGv4rNwCPjbY3ziYR01E6UV+TVgCFcbQ==",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.15.0",
|
||||
"@langchain/core": "~0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@langchain/anthropic/node_modules/@anthropic-ai/sdk": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.15.0.tgz",
|
||||
"integrity": "sha512-QMNEFcwGGB64oEIL+U9b+mxSbat5TCdNxvQVV0qCNGQvg/nlnbOmq2/x/0mKhuKD0n5bioL75oCkTbQaAgyYtw==",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"abort-controller": "^3.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"digest-fetch": "^1.3.0",
|
||||
"form-data-encoder": "1.7.2",
|
||||
"formdata-node": "^4.3.2",
|
||||
"node-fetch": "^2.6.7",
|
||||
"web-streams-polyfill": "^3.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@langchain/anthropic/node_modules/@types/node": {
|
||||
"version": "18.19.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.21.tgz",
|
||||
"integrity": "sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@langchain/cohere": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/cohere/-/cohere-0.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
"dependencies": {
|
||||
"@huggingface/inference": "^2.6.4",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@langchain/anthropic": "^0.1.1",
|
||||
"@langchain/cohere": "^0.0.2",
|
||||
"@langchain/community": "^0.0.16",
|
||||
"@langchain/google-genai": "^0.0.7",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { LangChainParams, ModelConfig } from '@/aiParams';
|
||||
import {
|
||||
ANTHROPIC_MODELS,
|
||||
AZURE_MODELS,
|
||||
GOOGLE_MODELS,
|
||||
LM_STUDIO_MODELS,
|
||||
|
|
@ -7,10 +8,12 @@ import {
|
|||
OLLAMA_MODELS,
|
||||
OPENAI_MODELS,
|
||||
OPENROUTERAI_MODELS,
|
||||
PROXY_SERVER_PORT,
|
||||
} from '@/constants';
|
||||
import EncryptionService from '@/encryptionService';
|
||||
import { ProxyChatOpenAI } from '@/langchainWrappers';
|
||||
import { getModelName } from '@/utils';
|
||||
import { ChatAnthropic } from "@langchain/anthropic";
|
||||
import { ChatOllama } from "@langchain/community/chat_models/ollama";
|
||||
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
||||
import { BaseChatModel } from 'langchain/chat_models/base';
|
||||
|
|
@ -69,7 +72,9 @@ export default class ChatModelManager {
|
|||
openAIProxyBaseUrl: params.openAIProxyBaseUrl,
|
||||
},
|
||||
[ModelProviders.ANTHROPIC]: {
|
||||
anthropicApiUrl: `http://localhost:${PROXY_SERVER_PORT}`,
|
||||
anthropicApiKey: decrypt(params.anthropicApiKey),
|
||||
modelName: params.anthropicModel,
|
||||
},
|
||||
[ModelProviders.AZURE_OPENAI]: {
|
||||
maxTokens: params.maxTokens,
|
||||
|
|
@ -125,6 +130,12 @@ export default class ChatModelManager {
|
|||
constructor: ChatGoogleGenerativeAI,
|
||||
vendor: ModelProviders.GOOGLE,
|
||||
},
|
||||
{
|
||||
models: ANTHROPIC_MODELS,
|
||||
apiKey: this.langChainParams.anthropicApiKey,
|
||||
constructor: ChatAnthropic,
|
||||
vendor: ModelProviders.ANTHROPIC,
|
||||
},
|
||||
{
|
||||
models: OPENROUTERAI_MODELS,
|
||||
apiKey: this.langChainParams.openRouterAiApiKey,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export interface ModelConfig {
|
|||
maxTokens?: number,
|
||||
openAIApiKey?: string,
|
||||
anthropicApiKey?: string,
|
||||
anthropicModel?: string,
|
||||
azureOpenAIApiKey?: string,
|
||||
azureOpenAIApiInstanceName?: string,
|
||||
azureOpenAIApiDeploymentName?: string,
|
||||
|
|
@ -30,6 +31,7 @@ export interface LangChainParams {
|
|||
huggingfaceApiKey: string,
|
||||
cohereApiKey: string,
|
||||
anthropicApiKey: string,
|
||||
anthropicModel: string,
|
||||
azureOpenAIApiKey: string,
|
||||
azureOpenAIApiInstanceName: string,
|
||||
azureOpenAIApiDeploymentName: string,
|
||||
|
|
|
|||
|
|
@ -1,31 +1,26 @@
|
|||
import { SetChainOptions } from '@/aiParams';
|
||||
import { SetChainOptions } from "@/aiParams";
|
||||
import {
|
||||
AI_SENDER,
|
||||
ChatModelDisplayNames,
|
||||
PROXY_SERVER_PORT,
|
||||
VAULT_VECTOR_STORE_STRATEGY,
|
||||
} from '@/constants';
|
||||
import {
|
||||
ChatMessage
|
||||
} from '@/sharedState';
|
||||
import {
|
||||
getFileContent,
|
||||
getFileName,
|
||||
} from '@/utils';
|
||||
import { Notice, Vault } from 'obsidian';
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
} from "@/constants";
|
||||
import { ProxyServer } from "@/proxyServer";
|
||||
import { ChatMessage } from "@/sharedState";
|
||||
import { getFileContent, getFileName } from "@/utils";
|
||||
import { Notice, Vault } from "obsidian";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { ChainType } from '@/chainFactory';
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import {
|
||||
RefreshIcon, SaveAsNoteIcon,
|
||||
RefreshIcon,
|
||||
SaveAsNoteIcon,
|
||||
SendActiveNoteToPromptIcon,
|
||||
StopIcon,
|
||||
UseActiveNoteAsContextIcon,
|
||||
} from '@/components/Icons';
|
||||
import { stringToChainType } from '@/utils';
|
||||
import React from 'react';
|
||||
} from "@/components/Icons";
|
||||
import { stringToChainType } from "@/utils";
|
||||
import React from "react";
|
||||
|
||||
interface ChatIconsProps {
|
||||
currentModel: string;
|
||||
|
|
@ -59,32 +54,44 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
|
|||
vault_qa_strategy,
|
||||
}) => {
|
||||
const [selectedChain, setSelectedChain] = useState<ChainType>(currentChain);
|
||||
const proxyServer = new ProxyServer(PROXY_SERVER_PORT);
|
||||
|
||||
const handleModelChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const handleModelChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const selectedModel = event.target.value;
|
||||
setCurrentModel(event.target.value);
|
||||
|
||||
if (selectedModel === ChatModelDisplayNames.CLAUDE) {
|
||||
// Start the proxy server when CLAUDE is selected
|
||||
await proxyServer.startProxyServer('https://api.anthropic.com/');
|
||||
} else {
|
||||
// Stop the proxy server when another model is selected
|
||||
await proxyServer.stopProxyServer();
|
||||
}
|
||||
};
|
||||
|
||||
const handleChainChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const handleChainChange = async (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
setSelectedChain(stringToChainType(event.target.value));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleChainSelection = async () => {
|
||||
const handleChainSelection = async () => {
|
||||
if (!app) {
|
||||
console.error('App instance is not available.');
|
||||
console.error("App instance is not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedChain === ChainType.LONG_NOTE_QA_CHAIN) {
|
||||
const file = app.workspace.getActiveFile();
|
||||
if (!file) {
|
||||
new Notice('No active note found.');
|
||||
console.error('No active note found.');
|
||||
new Notice("No active note found.");
|
||||
console.error("No active note found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const noteContent = await getFileContent(file, vault);
|
||||
const fileMetadata = app.metadataCache.getFileCache(file)
|
||||
const fileMetadata = app.metadataCache.getFileCache(file);
|
||||
const noteFile = {
|
||||
path: file.path,
|
||||
basename: file.basename,
|
||||
|
|
@ -118,78 +125,124 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
|
|||
}
|
||||
|
||||
setCurrentChain(selectedChain);
|
||||
};
|
||||
};
|
||||
|
||||
handleChainSelection();
|
||||
handleChainSelection();
|
||||
}, [selectedChain]);
|
||||
|
||||
return (
|
||||
<div className='chat-icons-container'>
|
||||
<div className="chat-icons-container">
|
||||
<div className="chat-icon-selection-tooltip">
|
||||
<div className="select-wrapper">
|
||||
<select
|
||||
id="aiModelSelect"
|
||||
className='chat-icon-selection'
|
||||
className="chat-icon-selection"
|
||||
value={currentModel}
|
||||
onChange={handleModelChange}
|
||||
>
|
||||
<option value={ChatModelDisplayNames.GPT_35_TURBO}>{ChatModelDisplayNames.GPT_35_TURBO}</option>
|
||||
<option value={ChatModelDisplayNames.GPT_35_TURBO_16K}>{ChatModelDisplayNames.GPT_35_TURBO_16K}</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4}>{ChatModelDisplayNames.GPT_4}</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4_TURBO}>{ChatModelDisplayNames.GPT_4_TURBO}</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4_32K}>{ChatModelDisplayNames.GPT_4_32K}</option>
|
||||
<option value={ChatModelDisplayNames.AZURE_OPENAI}>{ChatModelDisplayNames.AZURE_OPENAI}</option>
|
||||
<option value={ChatModelDisplayNames.GEMINI_PRO}>{ChatModelDisplayNames.GEMINI_PRO}</option>
|
||||
<option value={ChatModelDisplayNames.OPENROUTERAI}>{ChatModelDisplayNames.OPENROUTERAI}</option>
|
||||
<option value={ChatModelDisplayNames.LM_STUDIO}>{ChatModelDisplayNames.LM_STUDIO}</option>
|
||||
<option value={ChatModelDisplayNames.OLLAMA}>{ChatModelDisplayNames.OLLAMA}</option>
|
||||
<option value={ChatModelDisplayNames.GPT_35_TURBO}>
|
||||
{ChatModelDisplayNames.GPT_35_TURBO}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.GPT_35_TURBO_16K}>
|
||||
{ChatModelDisplayNames.GPT_35_TURBO_16K}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4}>
|
||||
{ChatModelDisplayNames.GPT_4}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4_TURBO}>
|
||||
{ChatModelDisplayNames.GPT_4_TURBO}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.GPT_4_32K}>
|
||||
{ChatModelDisplayNames.GPT_4_32K}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.AZURE_OPENAI}>
|
||||
{ChatModelDisplayNames.AZURE_OPENAI}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.CLAUDE}>
|
||||
{ChatModelDisplayNames.CLAUDE}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.GEMINI_PRO}>
|
||||
{ChatModelDisplayNames.GEMINI_PRO}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.OPENROUTERAI}>
|
||||
{ChatModelDisplayNames.OPENROUTERAI}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.LM_STUDIO}>
|
||||
{ChatModelDisplayNames.LM_STUDIO}
|
||||
</option>
|
||||
<option value={ChatModelDisplayNames.OLLAMA}>
|
||||
{ChatModelDisplayNames.OLLAMA}
|
||||
</option>
|
||||
</select>
|
||||
<span className="tooltip-text">Model Selection</span>
|
||||
</div>
|
||||
</div>
|
||||
<button className='chat-icon-button' onClick={onStopGenerating}>
|
||||
<StopIcon className='icon-scaler' />
|
||||
<button className="chat-icon-button" onClick={onStopGenerating}>
|
||||
<StopIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">Stop Generating</span>
|
||||
</button>
|
||||
<button className='chat-icon-button' onClick={onNewChat}>
|
||||
<RefreshIcon className='icon-scaler' />
|
||||
<span className="tooltip-text">New Chat<br/>(unsaved history will be lost)</span>
|
||||
<button className="chat-icon-button" onClick={onNewChat}>
|
||||
<RefreshIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">
|
||||
New Chat
|
||||
<br />
|
||||
(unsaved history will be lost)
|
||||
</span>
|
||||
</button>
|
||||
<button className='chat-icon-button' onClick={onSaveAsNote}>
|
||||
<SaveAsNoteIcon className='icon-scaler' />
|
||||
<button className="chat-icon-button" onClick={onSaveAsNote}>
|
||||
<SaveAsNoteIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">Save as Note</span>
|
||||
</button>
|
||||
<div className="chat-icon-selection-tooltip">
|
||||
<div className="select-wrapper">
|
||||
<select
|
||||
id="aiChainSelect"
|
||||
className='chat-icon-selection'
|
||||
className="chat-icon-selection"
|
||||
value={currentChain}
|
||||
onChange={handleChainChange}
|
||||
>
|
||||
<option value='llm_chain'>Chat</option>
|
||||
<option value='long_note_qa'>Long Note QA</option>
|
||||
<option value='vault_qa'>Vault QA (BETA)</option>
|
||||
<option value="llm_chain">Chat</option>
|
||||
<option value="long_note_qa">Long Note QA</option>
|
||||
<option value="vault_qa">Vault QA (BETA)</option>
|
||||
</select>
|
||||
<span className="tooltip-text">Mode Selection</span>
|
||||
</div>
|
||||
</div>
|
||||
{selectedChain === 'llm_chain' && (
|
||||
<button className='chat-icon-button' onClick={onSendActiveNoteToPrompt}>
|
||||
<SendActiveNoteToPromptIcon className='icon-scaler' />
|
||||
<span className="tooltip-text">Send Note(s) to Prompt<br/>(Set with Copilot command: <br/>set note context <br/>in Chat mode.<br/>Default is active note)</span>
|
||||
{selectedChain === "llm_chain" && (
|
||||
<button className="chat-icon-button" onClick={onSendActiveNoteToPrompt}>
|
||||
<SendActiveNoteToPromptIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">
|
||||
Send Note(s) to Prompt
|
||||
<br />
|
||||
(Set with Copilot command: <br />
|
||||
set note context <br />
|
||||
in Chat mode.
|
||||
<br />
|
||||
Default is active note)
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{selectedChain === 'long_note_qa' && (
|
||||
<button className='chat-icon-button' onClick={onForceRebuildActiveNoteContext}>
|
||||
<UseActiveNoteAsContextIcon className='icon-scaler' />
|
||||
<span className="tooltip-text">Refresh Index<br/>for Active Note</span>
|
||||
{selectedChain === "long_note_qa" && (
|
||||
<button
|
||||
className="chat-icon-button"
|
||||
onClick={onForceRebuildActiveNoteContext}
|
||||
>
|
||||
<UseActiveNoteAsContextIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">
|
||||
Refresh Index
|
||||
<br />
|
||||
for Active Note
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{selectedChain === 'vault_qa' && (
|
||||
<button className='chat-icon-button' onClick={onRefreshVaultContext}>
|
||||
<UseActiveNoteAsContextIcon className='icon-scaler' />
|
||||
<span className="tooltip-text">Refresh Index<br/>for Vault</span>
|
||||
{selectedChain === "vault_qa" && (
|
||||
<button className="chat-icon-button" onClick={onRefreshVaultContext}>
|
||||
<UseActiveNoteAsContextIcon className="icon-scaler" />
|
||||
<span className="tooltip-text">
|
||||
Refresh Index
|
||||
<br />
|
||||
for Vault
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export enum ChatModelDisplayNames {
|
|||
GPT_4_TURBO = 'GPT-4 TURBO',
|
||||
GPT_4_32K = 'GPT-4 32K',
|
||||
AZURE_OPENAI = 'AZURE OPENAI',
|
||||
CLAUDE = 'CLAUDE 3',
|
||||
GEMINI_PRO = 'GEMINI PRO',
|
||||
OPENROUTERAI = 'OPENROUTER.AI',
|
||||
OLLAMA = 'OLLAMA (LOCAL)',
|
||||
|
|
@ -45,6 +46,10 @@ export const GOOGLE_MODELS = new Set([
|
|||
ChatModelDisplayNames.GEMINI_PRO,
|
||||
]);
|
||||
|
||||
export const ANTHROPIC_MODELS = new Set([
|
||||
ChatModelDisplayNames.CLAUDE,
|
||||
]);
|
||||
|
||||
export const OPENROUTERAI_MODELS = new Set([
|
||||
ChatModelDisplayNames.OPENROUTERAI,
|
||||
])
|
||||
|
|
@ -84,6 +89,7 @@ export const VENDOR_MODELS: Record<string, Set<string>> = {
|
|||
[ModelProviders.OPENAI]: OPENAI_MODELS,
|
||||
[ModelProviders.AZURE_OPENAI]: AZURE_MODELS,
|
||||
[ModelProviders.GOOGLE]: GOOGLE_MODELS,
|
||||
[ModelProviders.ANTHROPIC]: ANTHROPIC_MODELS,
|
||||
[ModelProviders.OPENROUTERAI]: OPENROUTERAI_MODELS,
|
||||
[ModelProviders.OLLAMA]: OLLAMA_MODELS,
|
||||
[ModelProviders.LM_STUDIO]: LM_STUDIO_MODELS,
|
||||
|
|
@ -133,11 +139,14 @@ export const VAULT_VECTOR_STORE_STRATEGIES = [
|
|||
VAULT_VECTOR_STORE_STRATEGY.ON_MODE_SWITCH,
|
||||
];
|
||||
|
||||
export const PROXY_SERVER_PORT = 53001;
|
||||
|
||||
export const DEFAULT_SETTINGS: CopilotSettings = {
|
||||
openAIApiKey: '',
|
||||
huggingfaceApiKey: '',
|
||||
cohereApiKey: '',
|
||||
anthropicApiKey: '',
|
||||
anthropicModel: 'claude-3-sonnet-20240229',
|
||||
azureOpenAIApiKey: '',
|
||||
azureOpenAIApiInstanceName: '',
|
||||
azureOpenAIApiDeploymentName: '',
|
||||
|
|
|
|||
|
|
@ -650,6 +650,7 @@ export default class CopilotPlugin extends Plugin {
|
|||
huggingfaceApiKey,
|
||||
cohereApiKey,
|
||||
anthropicApiKey,
|
||||
anthropicModel,
|
||||
azureOpenAIApiKey,
|
||||
azureOpenAIApiInstanceName,
|
||||
azureOpenAIApiDeploymentName,
|
||||
|
|
@ -671,6 +672,7 @@ export default class CopilotPlugin extends Plugin {
|
|||
huggingfaceApiKey,
|
||||
cohereApiKey,
|
||||
anthropicApiKey,
|
||||
anthropicModel: anthropicModel || DEFAULT_SETTINGS.anthropicModel,
|
||||
azureOpenAIApiKey,
|
||||
azureOpenAIApiInstanceName,
|
||||
azureOpenAIApiDeploymentName,
|
||||
|
|
|
|||
65
src/proxyServer.ts
Normal file
65
src/proxyServer.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import cors from '@koa/cors';
|
||||
import Koa from 'koa';
|
||||
import proxy from 'koa-proxies';
|
||||
import net from 'net';
|
||||
|
||||
export class ProxyServer {
|
||||
private port: number;
|
||||
private server?: net.Server;
|
||||
|
||||
constructor(port: number) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
async startProxyServer(proxyBaseUrl: string) {
|
||||
console.log('loading plugin');
|
||||
// check if the port is already in use
|
||||
const inUse = await this.checkPortInUse(this.port);
|
||||
|
||||
if (!inUse) {
|
||||
// Create a new Koa application
|
||||
const app = new Koa();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
// Create and apply the proxy middleware
|
||||
app.use(proxy('/', {
|
||||
// your target API, e.g. https://api.anthropic.com/
|
||||
target: proxyBaseUrl,
|
||||
changeOrigin: true,
|
||||
}));
|
||||
|
||||
// Start the server on the specified port
|
||||
this.server = app.listen(this.port);
|
||||
console.log(`Proxy server running on http://localhost:${this.port}`);
|
||||
} else {
|
||||
console.error(`Port ${this.port} is in use`);
|
||||
}
|
||||
}
|
||||
|
||||
async stopProxyServer() {
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkPortInUse(port: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer()
|
||||
.once('error', (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
resolve(true); // Port is in use
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
.once('listening', () => {
|
||||
server.once('close', () => {
|
||||
resolve(false); // Port is not in use
|
||||
})
|
||||
.close();
|
||||
})
|
||||
.listen(port);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ export interface CopilotSettings {
|
|||
huggingfaceApiKey: string;
|
||||
cohereApiKey: string;
|
||||
anthropicApiKey: string;
|
||||
anthropicModel: string;
|
||||
azureOpenAIApiKey: string;
|
||||
azureOpenAIApiInstanceName: string;
|
||||
azureOpenAIApiDeploymentName: string;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ interface ApiSettingsProps {
|
|||
setOpenAIApiKey: (value: string) => void;
|
||||
googleApiKey: string;
|
||||
setGoogleApiKey: (value: string) => void;
|
||||
anthropicApiKey: string;
|
||||
setAnthropicApiKey: (value: string) => void;
|
||||
anthropicModel: string;
|
||||
setAnthropicModel: (value: string) => void;
|
||||
openRouterAiApiKey: string;
|
||||
setOpenRouterAiApiKey: (value: string) => void;
|
||||
openRouterModel: string;
|
||||
|
|
@ -29,6 +33,10 @@ const ApiSettings: React.FC<ApiSettingsProps> = ({
|
|||
setOpenAIApiKey,
|
||||
googleApiKey,
|
||||
setGoogleApiKey,
|
||||
anthropicApiKey,
|
||||
setAnthropicApiKey,
|
||||
anthropicModel,
|
||||
setAnthropicModel,
|
||||
openRouterAiApiKey,
|
||||
setOpenRouterAiApiKey,
|
||||
openRouterModel,
|
||||
|
|
@ -100,6 +108,33 @@ const ApiSettings: React.FC<ApiSettingsProps> = ({
|
|||
</div>
|
||||
</Collapsible>
|
||||
|
||||
<Collapsible title="Anthropic API Settings">
|
||||
<div>
|
||||
<ApiSetting
|
||||
title="Anthropic API Key"
|
||||
value={anthropicApiKey}
|
||||
setValue={setAnthropicApiKey}
|
||||
placeholder="Enter Anthropic API Key"
|
||||
/>
|
||||
<ApiSetting
|
||||
title="Anthropic Model"
|
||||
value={anthropicModel}
|
||||
// @ts-ignore
|
||||
setValue={setAnthropicModel}
|
||||
placeholder={DEFAULT_SETTINGS.anthropicModel}
|
||||
type="text"
|
||||
/>
|
||||
<p>
|
||||
If you have Anthropic API access, you can get the API key {' '}
|
||||
<a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener noreferrer">
|
||||
here
|
||||
</a>.
|
||||
<br />
|
||||
Your API key is stored locally and is only used to make requests to Anthropic's services.
|
||||
</p>
|
||||
</div>
|
||||
</Collapsible>
|
||||
|
||||
<Collapsible title="OpenRouter.ai API Settings">
|
||||
<div>
|
||||
<ApiSetting
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps
|
|||
const [openAIApiKey, setOpenAIApiKey] = useState(plugin.settings.openAIApiKey);
|
||||
const [googleApiKey, setGoogleApiKey] = useState(plugin.settings.googleApiKey);
|
||||
|
||||
const [anthropicApiKey, setAnthropicApiKey] = useState(plugin.settings.anthropicApiKey);
|
||||
const [anthropicModel, setAnthropicModel] = useState(plugin.settings.anthropicModel);
|
||||
|
||||
const [openRouterAiApiKey, setOpenRouterAiApiKey] = useState(plugin.settings.openRouterAiApiKey);
|
||||
const [openRouterModel, setOpenRouterModel] = useState(plugin.settings.openRouterModel);
|
||||
|
||||
|
|
@ -64,6 +67,8 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps
|
|||
// API settings
|
||||
plugin.settings.openAIApiKey = openAIApiKey;
|
||||
plugin.settings.googleApiKey = googleApiKey;
|
||||
plugin.settings.anthropicApiKey = anthropicApiKey;
|
||||
plugin.settings.anthropicModel = anthropicModel;
|
||||
plugin.settings.openRouterAiApiKey = openRouterAiApiKey;
|
||||
plugin.settings.openRouterModel = openRouterModel;
|
||||
plugin.settings.azureOpenAIApiKey = azureOpenAIApiKey;
|
||||
|
|
@ -180,6 +185,10 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps
|
|||
setOpenAIApiKey={setOpenAIApiKey}
|
||||
googleApiKey={googleApiKey}
|
||||
setGoogleApiKey={setGoogleApiKey}
|
||||
anthropicApiKey={anthropicApiKey}
|
||||
setAnthropicApiKey={setAnthropicApiKey}
|
||||
anthropicModel={anthropicModel}
|
||||
setAnthropicModel={setAnthropicModel}
|
||||
openRouterAiApiKey={openRouterAiApiKey}
|
||||
setOpenRouterAiApiKey={setOpenRouterAiApiKey}
|
||||
openRouterModel={openRouterModel}
|
||||
|
|
|
|||
Loading…
Reference in a new issue