Skip to content

Commit

Permalink
feat: add continue generate support, fix some bug about third-party m…
Browse files Browse the repository at this point in the history
…odels error response
  • Loading branch information
2214962083 committed Jul 9, 2024
1 parent d4ebe3e commit 9c5e3e5
Show file tree
Hide file tree
Showing 18 changed files with 824 additions and 628 deletions.
5 changes: 4 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
"--disable-extensions"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: watch"
"preLaunchTask": "npm: watch",
"env": {
// "NODE_DEBUG": "http"
}
}
]
}
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@
"@types/node": "^20.14.10",
"@types/shell-quote": "^1.7.5",
"@types/vscode": "1.82.0",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.16.0",
"@vscode/vsce": "^2.29.0",
"commitizen": "^4.3.0",
"dotenv": "^16.4.5",
Expand All @@ -241,19 +241,19 @@
"global-agent": "^3.0.0",
"husky": "^9.0.11",
"inquirer": "^9.3.4",
"knip": "^5.24.2",
"knip": "^5.25.1",
"langchain": "^0.2.8",
"lint-staged": "^15.2.7",
"minimatch": "^9.0.5",
"pnpm": "^9.5.0",
"prettier": "^3.3.2",
"rimraf": "^5.0.8",
"rimraf": "^6.0.0",
"shell-quote": "^1.8.1",
"tsup": "^8.1.0",
"typescript": "5.4.5",
"undici": "^6.19.2",
"vite": "^5.3.3",
"vitest": "^1.6.0"
"vitest": "^2.0.1"
},
"pnpm": {
"overrides": {
Expand Down
2 changes: 2 additions & 0 deletions package.nls.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"error.vscodeLLMModelNotFound": "VSCode LLM model not found, please check configuration",
"error.noSelection": "No file or folder selected",
"error.noActiveEditor": "No file is currently open",
"error.noTargetLanguage": "No target language selected",
"error.noContext": "Context not initialized",
"info.copied": "File contents have been copied to clipboard",
"input.array.promptEnding": "Enter comma separated values",
"input.json.promptEnding": "Enter JSON formatted value",
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"error.vscodeLLMModelNotFound": "VSCode LLM model not found, please check configuration",
"error.noSelection": "No file or folder selected",
"error.noActiveEditor": "No file is currently open",
"error.noTargetLanguage": "No target language selected",
"error.noContext": "Context not initialized",
"info.copied": "File contents have been copied to clipboard",
"input.array.promptEnding": "Enter comma separated values",
"input.json.promptEnding": "Enter JSON formatted value",
Expand Down
2 changes: 2 additions & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"error.vscodeLLMModelNotFound": "未找到 VSCode LLM 模型,请检查配置",
"error.noSelection": "未选择任何文件或文件夹",
"error.noActiveEditor": "未打开任何文件",
"error.noTargetLanguage": "未选择目标语言",
"error.noContext": "上下文未初始化",
"info.copied": "文件内容已复制到剪贴板",
"input.array.promptEnding": "输入逗号分隔的值",
"input.json.promptEnding": "输入 JSON 格式的值",
Expand Down
658 changes: 229 additions & 429 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions src/ai/llm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { getConfigKey } from '@/config'
import { getContext } from '@/context'
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history'
import { type BaseMessage, type MessageContent } from '@langchain/core/messages'
import {
ChatPromptTemplate,
HumanMessagePromptTemplate,
MessagesPlaceholder
} from '@langchain/core/prompts'
import { RunnableWithMessageHistory } from '@langchain/core/runnables'
import { ChatOpenAI } from '@langchain/openai'
import * as vscode from 'vscode'

export const sessionIdHistoriesMap: Record<string, InMemoryChatMessageHistory> =
{}

export const createOpenAIRunnable = async (
historyMessages: BaseMessage[] = []
) => {
const isDev = getContext().extensionMode !== vscode.ExtensionMode.Production
const openaiBaseUrl = await getConfigKey('openaiBaseUrl')
const openaiKey = await getConfigKey('openaiKey')
const openaiModel = await getConfigKey('openaiModel')

const model = new ChatOpenAI({
apiKey: openaiKey,
configuration: {
baseURL: openaiBaseUrl,
fetch
},
model: openaiModel,
temperature: 0.95, // never use 1.0, some models do not support it
verbose: isDev
})

// some third-party language models are not compatible with the openAI specification,
// they do not support some parameters, and langchain takes the initiative to add these parameters,
// resulting in request failure, so here you need to clear these parameters
model.frequencyPenalty = undefined as any
model.n = undefined as any
model.presencePenalty = undefined as any
model.topP = undefined as any

const prompt = ChatPromptTemplate.fromMessages([
new MessagesPlaceholder('history'),
HumanMessagePromptTemplate.fromTemplate('{input}')
])

const chain = prompt.pipe(model)

const withMessageHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: async sessionId => {
if (sessionIdHistoriesMap[sessionId] === undefined) {
const messageHistory = new InMemoryChatMessageHistory()

if (historyMessages.length > 0) {
await messageHistory.addMessages(historyMessages)
}

sessionIdHistoriesMap[sessionId] = messageHistory
}

return sessionIdHistoriesMap[sessionId]!
},
inputMessagesKey: 'input',
historyMessagesKey: 'history'
})

return withMessageHistory
}

export const openaiAnswerContentToText = (content: MessageContent): string => {
if (typeof content === 'string') return content

return content
.map(c => {
if (c.type === 'text') return c.text
return ''
})
.join('')
}
60 changes: 60 additions & 0 deletions src/ai/tmp-file-writer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
createTmpFileAndWriter,
type CreateTmpFileOptions
} from '@/create-tmp-file'
import {
removeCodeBlockEndSyntax,
removeCodeBlockStartSyntax,
removeCodeBlockSyntax
} from '@/utils'
import type { IterableReadableStream } from '@langchain/core/dist/utils/stream'
import type { AIMessageChunk } from '@langchain/core/messages'

import { openaiAnswerContentToText } from './llm'

export interface TmpFileWriterOptions extends CreateTmpFileOptions {
buildAiStream: () => Promise<IterableReadableStream<AIMessageChunk>>
}

export const tmpFileWriter = async (options: TmpFileWriterOptions) => {
const { buildAiStream, ...createTmpFileOptions } = options

const { writeTextPart, getText, writeText, isClosedWithoutSaving } =
await createTmpFileAndWriter(createTmpFileOptions)

const aiStream = await buildAiStream()
for await (const chunk of aiStream) {
if (isClosedWithoutSaving()) return

// convert openai answer content to text
const text = openaiAnswerContentToText(chunk.content)
await writeTextPart(text)

// remove code block syntax
// for example, remove ```python\n and \n```
const currentCode = getText()
const removeCodeBlockStartSyntaxCode =
removeCodeBlockStartSyntax(currentCode)

if (removeCodeBlockStartSyntaxCode !== currentCode) {
await writeText(removeCodeBlockStartSyntaxCode)
}
}

// remove code block end syntax
// for example, remove \n``` at the end
const currentCode = getText()
const removeCodeBlockEndSyntaxCode = removeCodeBlockEndSyntax(currentCode)

if (removeCodeBlockEndSyntaxCode !== currentCode) {
await writeText(removeCodeBlockEndSyntaxCode)
}

// remove code block syntax
// for example, remove ```python\n and \n``` at the start and end
// just confirm the code is clean
const finalCode = removeCodeBlockSyntax(getText())

// write the final code
await writeText(finalCode)
}
13 changes: 13 additions & 0 deletions src/cleanup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as vscode from 'vscode'

import { cleanupCodeConvertRunnables } from './commands/code-convert'
import { cleanupCodeViewerHelperRunnables } from './commands/code-viewer-helper'

export const cleanup = async (context: vscode.ExtensionContext) => {
context.subscriptions.push(
vscode.workspace.onDidCloseTextDocument(() => {
cleanupCodeConvertRunnables()
cleanupCodeViewerHelperRunnables()
})
)
}
Loading

0 comments on commit 9c5e3e5

Please sign in to comment.