diff --git a/package-lock.json b/package-lock.json index d390b25..a4ee16d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", "string_score": "^0.1.22", - "symmetry-core": "^1.0.19", + "symmetry-core": "^1.0.22", "tippy.js": "^6.3.7", "tiptap-markdown": "^0.8.10", "toxe": "^1.1.0", @@ -12968,9 +12968,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "optional": true, "engines": { "node": ">= 0.6" @@ -15968,9 +15968,9 @@ } }, "node_modules/symmetry-core": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/symmetry-core/-/symmetry-core-1.0.19.tgz", - "integrity": "sha512-f8BIzW6kIiFEAAhcjdSF7vqrL23wCAgtmpdbDmdScFb4Txa4tqQUe0RwqZh59jL963FpFPa4zCGSfjqRr//0rw==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/symmetry-core/-/symmetry-core-1.0.22.tgz", + "integrity": "sha512-jSC4z8bMiztf0pUwZAsvsS6YRSGGIRdhKXsBzpzK2mP7RfZq0/0bjI/AlhfeARprr8vvnHrrwjF06gaM6M6qpA==", "dependencies": { "chalk": "^4.1.2", "commander": "^12.1.0", diff --git a/package.json b/package.json index c25158f..edf8b2a 100644 --- a/package.json +++ b/package.json @@ -512,7 +512,7 @@ "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", "string_score": "^0.1.22", - "symmetry-core": "^1.0.19", + "symmetry-core": "^1.0.22", "tippy.js": "^6.3.7", "tiptap-markdown": "^0.8.10", "toxe": "^1.1.0", diff --git a/src/extension/symmetry-service.ts b/src/extension/symmetry-service.ts index 2055549..d5aafb6 100644 --- a/src/extension/symmetry-service.ts +++ b/src/extension/symmetry-service.ts @@ -7,7 +7,11 @@ import yaml from "js-yaml" import os from "os" import path from "path" import { EventEmitter } from "stream" -import { serverMessageKeys, SymmetryProvider } from "symmetry-core" +import { + ProviderConfig, + serverMessageKeys, + SymmetryClient +} from "symmetry-core" import { commands, ExtensionContext, Webview, workspace } from "vscode" import { @@ -44,7 +48,7 @@ export class SymmetryService extends EventEmitter { private _config = workspace.getConfiguration("twinny") private _completion = "" private _context: ExtensionContext - private _provider: SymmetryProvider | undefined + private _client: SymmetryClient | undefined private _providerPeer: undefined | Peer private _providerSwarm: undefined | typeof Hyperswarm private _providerTopic: Buffer | undefined @@ -246,7 +250,7 @@ export class SymmetryService extends EventEmitter { return path.join(homeDir, ".config", "symmetry", "provider.yaml") } - private createProviderConfig(provider: TwinnyProvider): Promise { + private createProviderConfig(provider: TwinnyProvider): ProviderConfig { const configPath = this.getSymmetryConfigPath() const configDir = path.dirname(configPath) @@ -254,58 +258,103 @@ export class SymmetryService extends EventEmitter { fs.mkdirSync(configDir, { recursive: true }) } - const symmetryConfiguration = yaml.dump({ + const config: ProviderConfig = { apiHostname: provider.apiHostname, apiKey: provider.apiKey, - apiPath: provider.apiPath, - apiPort: provider.apiPort, + apiBasePath: provider.apiPath, + apiChatPath: provider.apiPath, + dataPath: configDir, + apiPort: provider.apiPort || 8080, apiProtocol: provider.apiProtocol, apiProvider: provider.provider, dataCollectionEnabled: false, maxConnections: 10, modelName: provider.modelName, name: os.hostname(), - path: configPath, public: true, serverKey: this._config.symmetryServerKey, - systemMessage: "" - }) + systemMessage: "", + userSecret: "", + } - return fs.promises.writeFile(configPath, symmetryConfiguration, "utf8") - } + const symmetryConfiguration = yaml.dump(config) - public startSymmetryProvider = async () => { - const provider = this.getChatProvider() + fs.promises.writeFile(configPath, symmetryConfiguration, "utf8") - if (!provider) return + return config + } + private async readProviderConfig(): Promise { const configPath = this.getSymmetryConfigPath() + const configStr = await fs.promises.readFile(configPath, "utf8") + return yaml.load(configStr) as ProviderConfig + } + + private updateProviderConfig = async (provider: TwinnyProvider): Promise => { + const configPath = this.getSymmetryConfigPath() + const configDir = path.dirname(configPath) - if (!fs.existsSync(configPath)) { - await this.createProviderConfig(provider) + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true }) } - this._provider = new SymmetryProvider(configPath) + try { + if (fs.existsSync(configPath)) { + const backupPath = `${configPath}.backup-${Date.now()}` + await fs.promises.copyFile(configPath, backupPath) + } - const sessionKey = EXTENSION_SESSION_NAME.twinnySymmetryConnectionProvider + let config: ProviderConfig + if (fs.existsSync(configPath)) { + config = await this.readProviderConfig() + const updates: Partial = {} + if (!config.apiChatPath) updates.apiChatPath = provider.apiPath + if (!config.dataPath) updates.dataPath = configDir + + const updatedConfig = { ...config, ...updates } + await fs.promises.writeFile(configPath, yaml.dump(updatedConfig), "utf8") + } else { + config = this.createProviderConfig(this.getChatProvider() as TwinnyProvider) + await fs.promises.writeFile(configPath, yaml.dump(config), "utf8") + } + } catch (error) { + console.error("Failed to update config:", error) + throw error + } + } - this._sessionManager?.set(sessionKey, "connecting") + public startSymmetryProvider = async () => { + const provider = this.getChatProvider() + if (!provider) return - const sessionTypeName = `${EVENT_NAME.twinnySessionContext}-${sessionKey}` + try { + await this.updateProviderConfig(provider) - this._webView?.postMessage({ - type: sessionTypeName, - value: "connecting" - }) + this._client = new SymmetryClient(this.getSymmetryConfigPath()) - await this._provider.init() + const sessionKey = EXTENSION_SESSION_NAME.twinnySymmetryConnectionProvider + this._sessionManager?.set(sessionKey, "connecting") - this._sessionManager?.set(sessionKey, "connected") + const sessionTypeName = `${EVENT_NAME.twinnySessionContext}-${sessionKey}` + this._webView?.postMessage({ + type: sessionTypeName, + value: "connecting" + }) - this._webView?.postMessage({ - type: sessionTypeName, - value: "connected" - }) + await this._client.init() + + this._sessionManager?.set(sessionKey, "connected") + this._webView?.postMessage({ + type: sessionTypeName, + value: "connected" + }) + } catch (error) { + console.error("Failed to start provider:", error) + this._sessionManager?.set( + EXTENSION_SESSION_NAME.twinnySymmetryConnectionProvider, + "error" + ) + } } private notifyWebView(type: string, value: any = {}) { @@ -320,7 +369,7 @@ export class SymmetryService extends EventEmitter { } public stopSymmetryProvider = async () => { - await this._provider?.destroySwarms() + await this._client?.destroySwarms() updateSymmetryStatus(this._webView, "disconnected") const sessionKey = EXTENSION_SESSION_NAME.twinnySymmetryConnectionProvider this._sessionManager?.set(sessionKey, "disconnected")