Skip to content

Commit

Permalink
feat: support query Aide key usage info when click vscode statusbar a…
Browse files Browse the repository at this point in the history
…ide item
  • Loading branch information
2214962083 committed Aug 8, 2024
1 parent 43a8049 commit b580fcd
Show file tree
Hide file tree
Showing 18 changed files with 314 additions and 10 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
// "--disable-extensions"
"--extensionDevelopmentPath=${workspaceFolder}",
"--disable-extensions"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: watch",
Expand Down
11 changes: 11 additions & 0 deletions package.nls.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"error.xclipNotFound": "xclip is not installed. Please install it using your package manager (e.g., sudo apt-get install xclip)",
"error.fileNotFound": "File not found",
"error.invalidInput": "Invalid input",
"error.aideKeyUsageInfoOnlySupportAideModels": "We currently only support viewing the usage information of the Aide model aggregation service. Please check: [https://aide.nicepkg.cn/guide/use-another-llm/aide-models](https://aide.nicepkg.cn/guide/use-another-llm/aide-models)",
"info.copied": "File contents have been copied to clipboard",
"info.noAiSuggestionsVariableName": "AI thinks your variable name is already good",
"info.processing": "Aide is processing...",
Expand All @@ -55,6 +56,16 @@
"info.commandCopiedToClipboard": "AI command has been copied to clipboard",
"info.fileReplaceSuccess": "File content has been replaced successfully",
"info.batchProcessorSuccess": "AI batch processor success!\n\nTotal {0} files generated, you can review and replace manually.\n\nTasks completed:\n{1}",
"info.loading": "Loading...",
"info.aideKey.neverExpires": "never expires",
"info.aideKey.usageInfo": "Aide key usage information (unit: virtual dollars)",
"info.aideKey.total": "Key total",
"info.aideKey.used": "Key used",
"info.aideKey.remain": "Key balance",
"info.aideKey.callCount": "Call count",
"info.aideKey.validUntil": "Valid until",
"info.aideKeyUsageStatusBar.text": "Aide Usage",
"info.aideKeyUsageStatusBar.tooltip": "Click to view Aide Key Usage Information",
"input.array.promptEnding": "Enter comma separated values",
"input.json.promptEnding": "Enter JSON formatted value",
"input.aiCommand.prompt": "Enter question for AI command",
Expand Down
11 changes: 11 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"error.xclipNotFound": "xclip is not installed. Please install it using your package manager (e.g., sudo apt-get install xclip)",
"error.fileNotFound": "File not found",
"error.invalidInput": "Invalid input",
"error.aideKeyUsageInfoOnlySupportAideModels": "We currently only support viewing the usage information of the Aide model aggregation service. Please check: [https://aide.nicepkg.cn/guide/use-another-llm/aide-models](https://aide.nicepkg.cn/guide/use-another-llm/aide-models)",
"info.copied": "File contents have been copied to clipboard",
"info.noAiSuggestionsVariableName": "AI thinks your variable name is already good",
"info.processing": "Aide is processing...",
Expand All @@ -55,6 +56,16 @@
"info.commandCopiedToClipboard": "AI command has been copied to clipboard",
"info.fileReplaceSuccess": "File content has been replaced successfully",
"info.batchProcessorSuccess": "AI batch processor success!\n\nTotal {0} files generated, you can review and replace manually.\n\nTasks completed:\n{1}",
"info.loading": "Loading...",
"info.aideKey.neverExpires": "never expires",
"info.aideKey.usageInfo": "Aide key usage information (unit: virtual dollars)",
"info.aideKey.total": "Key total",
"info.aideKey.used": "Key used",
"info.aideKey.remain": "Key balance",
"info.aideKey.callCount": "Call count",
"info.aideKey.validUntil": "Valid until",
"info.aideKeyUsageStatusBar.text": "Aide Usage",
"info.aideKeyUsageStatusBar.tooltip": "Click to view Aide Key Usage Information",
"input.array.promptEnding": "Enter comma separated values",
"input.json.promptEnding": "Enter JSON formatted value",
"input.aiCommand.prompt": "Enter question for AI command",
Expand Down
11 changes: 11 additions & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"error.xclipNotFound": "xclip 未安装。请使用你的包管理器安装它 (例如,sudo apt-get install xclip)",
"error.fileNotFound": "文件未找到",
"error.invalidInput": "无效的输入",
"error.aideKeyUsageInfoOnlySupportAideModels": "我们目前仅支持查看 Aide 模型聚合服务的使用信息。请查看:[https://aide.nicepkg.cn/zh/guide/use-another-llm/aide-models](https://aide.nicepkg.cn/zh/guide/use-another-llm/aide-models)",
"info.copied": "文件内容已复制到剪贴板",
"info.noAiSuggestionsVariableName": " AI 觉得你这个变量名字已经很好了",
"info.processing": "Aide 正在处理中...",
Expand All @@ -55,6 +56,16 @@
"info.commandCopiedToClipboard": "AI 命令已复制到剪贴板",
"info.fileReplaceSuccess": "文件内容已成功替换",
"info.batchProcessorSuccess": "AI 批量处理成功!\n\n共生成了 {0} 个文件, 你可以自己 review 手动替换。\n\n已完成任务:\n{1}",
"info.loading": "加载中...",
"info.aideKey.neverExpires": "永不过期",
"info.aideKey.usageInfo": "Aide 密钥使用信息(单位:虚拟美元)",
"info.aideKey.total": "密钥总额",
"info.aideKey.used": "密钥消耗",
"info.aideKey.remain": "密钥余额",
"info.aideKey.callCount": "调用次数",
"info.aideKey.validUntil": "有效期至",
"info.aideKeyUsageStatusBar.text": "Aide 消耗",
"info.aideKeyUsageStatusBar.tooltip": "点击查看 Aide 密钥消耗信息",
"input.array.promptEnding": "输入逗号分隔的值",
"input.json.promptEnding": "输入 JSON 格式的值",
"input.aiCommand.prompt": "输入 AI 命令的问题",
Expand Down
170 changes: 170 additions & 0 deletions src/ai/aide-key-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// this file is for Aide third-party API query
type BaseRes<T = any> = {
success: boolean
data: T
message: string
}

type AideKeyUsageSearchParams = {
key: string
p: number // page
pageSize: number
}

type AideKeyUsageSearchResDataItem = {
id: number
request_id: string
user_id: number
created_at: number
type: number
content: string
username: string
token_name: string
model_name: string
channel_name: string
quota: number
prompt_tokens: number
completion_tokens: number
channel: number
token_key: string
request_duration: number
response_first_byte_duration: number
total_duration: number
duration_for_view: number
is_stream: boolean
ip: string
}

type AideKeyUsageSearchRes = BaseRes<{
total: number
data: AideKeyUsageSearchResDataItem[]
}>

export const aideKeyUsageSearch = (params: AideKeyUsageSearchParams) =>
fetch(
`https://api.zyai.online/public/log/self/search?key=${params.key}&p=${params.p}&pageSize=${params.pageSize}`,
{
method: 'GET'
}
).then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json() as Promise<AideKeyUsageSearchRes>
})

type AideKeyUsageCountParams = {
key: string
}

type AideKeyUsageCountResData = {
count: number
}

type AideKeyUsageCountRes = BaseRes<AideKeyUsageCountResData>

export const aideKeyUsageCount = (params: AideKeyUsageCountParams) =>
fetch(`https://api.zyai.online/public/log/self/count?key=${params.key}`, {
method: 'GET'
}).then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json() as Promise<AideKeyUsageCountRes>
})

type AideKeyUsageStatParams = {
key: string
}

type AideKeyUsageStatResData = {
mpm: number
quota: number
rpm: number
tpm: number
}

type AideKeyUsageStatRes = BaseRes<AideKeyUsageStatResData>

export const aideKeyUsageStat = (params: AideKeyUsageStatParams) =>
fetch(`https://api.zyai.online/public/log/self/stat?key=${params.key}`, {
method: 'GET'
}).then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json() as Promise<AideKeyUsageStatRes>
})

export type AideKeyUsageSubscriptionParams = {
key: string
}

export type AideKeyUsageSubscriptionRes = {
object: string
has_payment_method: boolean
soft_limit_usd: number
hard_limit_usd: number
system_hard_limit_usd: number
access_until: number
used_quota: number
remain_quota: number
used_count: number
}

export const aideKeyUsageSubscription = (
params: AideKeyUsageSubscriptionParams
) =>
fetch(`https://api.zyai.online/public/dashboard/billing/subscription`, {
method: 'GET',
headers: {
Authorization: `Bearer ${params.key}`
}
}).then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json() as Promise<AideKeyUsageSubscriptionRes>
})

type AideKeyUsageInfoParams = {
key: string
}

type AideKeyUsageInfoResData = {
count: AideKeyUsageCountResData
subscription: AideKeyUsageSubscriptionRes
}

type AideKeyUsageInfoRes = BaseRes<AideKeyUsageInfoResData>

export const aideKeyUsageInfo = (
params: AideKeyUsageInfoParams
): Promise<AideKeyUsageInfoRes> =>
Promise.all([aideKeyUsageCount(params), aideKeyUsageSubscription(params)])
.then(([countRes, subscriptionRes]) => ({
success: countRes.success,
data: {
count: countRes.data,
subscription: subscriptionRes
},
message: 'Combined usage info retrieved successfully'
}))
.catch(error => ({
success: false,
data: {
count: { count: 0 },
subscription: {
object: '',
has_payment_method: false,
soft_limit_usd: 0,
hard_limit_usd: 0,
system_hard_limit_usd: 0,
access_until: 0,
used_quota: 0,
remain_quota: 0,
used_count: 0
}
},
message: `Error fetching usage info: ${error.message}`
}))
10 changes: 9 additions & 1 deletion src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { handleCopyAsPrompt } from './copy-as-prompt'
import { handleCopyFileText } from './private/copy-file-text'
import { handleQuickCloseFileWithoutSave } from './private/quick-close-file-without-save'
import { handleReplaceFile } from './private/replace-file'
import { handleShowAideKeyUsageInfo } from './private/show-aide-key-usage-info'
import { handleShowDiff } from './private/show-diff'
import { handleRenameVariable } from './rename-variable'
import { handleSmartPaste } from './smart-paste'
Expand Down Expand Up @@ -72,6 +73,12 @@ export const registerCommands = async (context: vscode.ExtensionContext) => {
commandErrorCatcher(handleShowDiff)
)

// private command
const showAideKeyUsageInfoDisposable = vscode.commands.registerCommand(
'aide.showAideKeyUsageInfo',
commandErrorCatcher(handleShowAideKeyUsageInfo)
)

context.subscriptions.push(
copyDisposable,
askAIDisposable,
Expand All @@ -83,6 +90,7 @@ export const registerCommands = async (context: vscode.ExtensionContext) => {
copyFileTextDisposable,
quickCloseFileWithoutSaveDisposable,
replaceFileDisposable,
showDiffDisposable
showDiffDisposable,
showAideKeyUsageInfoDisposable
)
}
59 changes: 59 additions & 0 deletions src/commands/private/show-aide-key-usage-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { aideKeyUsageInfo } from '@/ai/aide-key-request'
import { getConfigKey } from '@/config'
import { t } from '@/i18n'
import { updateAideKeyUsageStatusBar } from '@/providers/aide-key-usage-statusbar'
import { formatNumber } from '@/utils'
import * as vscode from 'vscode'

export const handleShowAideKeyUsageInfo = async () => {
const openaiBaseUrl = await getConfigKey('openaiBaseUrl')
const openaiKey = await getConfigKey('openaiKey')

if (!openaiBaseUrl.includes('api.zyai.online'))
throw new Error(t('error.aideKeyUsageInfoOnlySupportAideModels'))

// show loading
updateAideKeyUsageStatusBar(`$(sync~spin) ${t('info.loading')}`)

try {
const result = await aideKeyUsageInfo({ key: openaiKey })

if (result.success) {
// create a nice message to show the result
const { count, subscription } = result.data

const totalUSD = subscription.hard_limit_usd
const usedUSD =
(subscription.used_quota / subscription.remain_quota) * totalUSD
const remainUSD = totalUSD - usedUSD
const formatUSD = (amount: number) => `$${formatNumber(amount, 2)}`
const formatDate = (timestamp: number) => {
if (timestamp === 0) return t('info.aideKey.neverExpires')
return new Date(timestamp * 1000).toLocaleDateString()
}

const message = `${t('info.aideKey.usageInfo')}:
${t('info.aideKey.total')}: ${formatUSD(totalUSD)}
${t('info.aideKey.used')}: ${formatUSD(usedUSD)}
${t('info.aideKey.remain')}: ${formatUSD(remainUSD)}
${t('info.aideKey.callCount')}: ${count.count}
${t('info.aideKey.validUntil')}: ${formatDate(subscription.access_until)}`

vscode.window.showInformationMessage(message, {
modal: true
})
} else {
throw new Error(`Failed to fetch usage info: ${result.message}`)
}
} finally {
// restore the original text of the status bar item
updateAideKeyUsageStatusBar(
`$(info) ${t('info.aideKeyUsageStatusBar.text')}`
)
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { initializeLocalization } from './i18n'
import { logger } from './logger'
import { enablePolyfill } from './polyfill'
import { registerProviders } from './providers'
import { initAideKeyUsageStatusBar } from './providers/aide-key-usage-statusbar'
import { redisStorage, stateStorage } from './storage'

export const activate = async (context: vscode.ExtensionContext) => {
Expand All @@ -26,6 +27,7 @@ export const activate = async (context: vscode.ExtensionContext) => {

await registerCommands(context)
await registerProviders(context)
await initAideKeyUsageStatusBar(context)
await autoOpenCorrespondingFiles(context)
await cleanup(context)
} catch (err) {
Expand Down
21 changes: 21 additions & 0 deletions src/providers/aide-key-usage-statusbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { t } from '@/i18n'
import * as vscode from 'vscode'

let aideKeyUsageStatusBar: vscode.StatusBarItem

export const initAideKeyUsageStatusBar = (context: vscode.ExtensionContext) => {
aideKeyUsageStatusBar = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right,
100
)
aideKeyUsageStatusBar.text = `$(info) ${t('info.aideKeyUsageStatusBar.text')}`
aideKeyUsageStatusBar.tooltip = t('info.aideKeyUsageStatusBar.tooltip')
aideKeyUsageStatusBar.command = 'aide.showAideKeyUsageInfo'
aideKeyUsageStatusBar.show()

context.subscriptions.push(aideKeyUsageStatusBar)
}

export const updateAideKeyUsageStatusBar = (text: string) => {
aideKeyUsageStatusBar.text = text
}
5 changes: 5 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ export const getCurrentWorkspaceFolderEditor = <T extends boolean = true>(
// }
// }

export const formatNumber = (num: number, fixed: number): string => {
const numString = num.toFixed(fixed)
return numString.replace(/\.?0+$/, '')
}

export const removeCodeBlockSyntax = (str: string): string => {
if (!str) return ''
return str
Expand Down
Loading

0 comments on commit b580fcd

Please sign in to comment.