feat: support agent tokens user agent

This commit is contained in:
jxxghp
2026-05-26 08:20:03 +08:00
parent 287ccf50b2
commit 96ac52041a
16 changed files with 101 additions and 55 deletions

4
.gitignore vendored
View File

@@ -11,6 +11,8 @@ __pycache__/
build/
develop-eggs/
dist/
!plugins.v2/agenttokens/dist/
!plugins.v2/agenttokens/dist/**
downloads/
eggs/
.eggs/
@@ -160,4 +162,4 @@ cython_debug/
#.idea/
.idea/
.vscode/
.vscode/

View File

@@ -1048,13 +1048,14 @@
"name": "Agent Tokens 管理",
"description": "管理多平台免费 Token 配额,按优先级自动切换 Agent LLM 供应商。",
"labels": "Agent,AI,系统",
"version": "1.0.7",
"version": "1.0.8",
"icon": "agentresourceofficer.png",
"author": "jxxghp",
"level": 1,
"system_version": ">=2.13.0",
"release": true,
"history": {
"v1.0.8": "支持为 Agent LLM 供应商配置并传递 User-Agent",
"v1.0.7": "禁用VWindow触摸滑动修复表格内滑动触发tab切换问题",
"v1.0.6": "优化标题样式并对齐站点管理页面风格,修复弹窗标题截断问题",
"v1.0.5": "优化UI布局修复页面标题和按钮滚动问题",

View File

@@ -24,7 +24,7 @@ class AgentTokens(_PluginBase):
plugin_name = "Agent Tokens 管理"
plugin_desc = "管理多平台免费 Token 配额,按优先级自动切换 Agent LLM 供应商。"
plugin_icon = "agentresourceofficer.png"
plugin_version = "1.0.7"
plugin_version = "1.0.8"
plugin_author = "jxxghp"
author_url = "https://github.com/jxxghp"
plugin_config_prefix = "agenttokens_"
@@ -212,6 +212,7 @@ class AgentTokens(_PluginBase):
) or "openai",
"base_url": cls._clean_text(provider.get("base_url")),
"api_key": cls._clean_text(provider.get("api_key")),
"user_agent": cls._clean_text(provider.get("user_agent")),
"model": cls._clean_text(provider.get("model")),
"token_limit": token_limit,
"used_tokens": used_tokens,
@@ -426,6 +427,7 @@ class AgentTokens(_PluginBase):
self._event_set(event.event_data, "provider", provider.get("provider") or "openai")
self._event_set(event.event_data, "base_url", provider.get("base_url"))
self._event_set(event.event_data, "api_key", provider.get("api_key"))
self._event_set(event.event_data, "user_agent", provider.get("user_agent"))
self._event_set(event.event_data, "model", provider.get("model"))
self._event_set(event.event_data, "base_url_preset", None)
self._event_set(event.event_data, "selected_provider_id", provider.get("id"))

View File

@@ -1,11 +1,11 @@
.gap-2[data-v-ff97075d] {
.gap-2[data-v-a0b44b7a] {
gap: 8px;
}
.progress-cell[data-v-ff97075d] {
.progress-cell[data-v-a0b44b7a] {
min-width: 140px;
}
.truncate-cell[data-v-ff97075d] {
.truncate-cell[data-v-a0b44b7a] {
max-width: 280px;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -80,6 +80,7 @@ function createProvider() {
provider: 'openai',
base_url: '',
api_key: '',
user_agent: '',
model: '',
token_limit: 0,
used_tokens: 0,
@@ -248,7 +249,7 @@ return (_ctx, _cache) => {
return (_openBlock(), _createElementBlock("div", _hoisted_1, [
(!__props.hideTitle)
? (_openBlock(), _createElementBlock("div", _hoisted_2, [
_cache[14] || (_cache[14] = _createElementVNode("h2", { class: "text-2xl font-bold leading-7 text-gray-100 truncate sm:text-3xl sm:leading-9" }, [
_cache[15] || (_cache[15] = _createElementVNode("h2", { class: "text-2xl font-bold leading-7 text-gray-100 truncate sm:text-3xl sm:leading-9" }, [
_createElementVNode("span", { class: "text-moviepilot" }, "Agent Tokens 管理")
], -1)),
_createVNode(_component_VSpacer),
@@ -336,7 +337,7 @@ return (_ctx, _cache) => {
class: "pa-4 h-100"
}, {
default: _withCtx(() => [
_cache[15] || (_cache[15] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "可用供应商", -1)),
_cache[16] || (_cache[16] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "可用供应商", -1)),
_createElementVNode("div", _hoisted_3, _toDisplayString(summary.value.available_count || 0) + " / " + _toDisplayString(summary.value.enabled_count || 0), 1)
]),
_: 1
@@ -355,7 +356,7 @@ return (_ctx, _cache) => {
class: "pa-4 h-100"
}, {
default: _withCtx(() => [
_cache[16] || (_cache[16] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "累计使用", -1)),
_cache[17] || (_cache[17] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "累计使用", -1)),
_createElementVNode("div", _hoisted_4, _toDisplayString(formatTokens(summary.value.total_used)), 1)
]),
_: 1
@@ -374,7 +375,7 @@ return (_ctx, _cache) => {
class: "pa-4 h-100"
}, {
default: _withCtx(() => [
_cache[17] || (_cache[17] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "总额度", -1)),
_cache[18] || (_cache[18] = _createElementVNode("div", { class: "text-caption text-medium-emphasis" }, "总额度", -1)),
_createElementVNode("div", _hoisted_5, _toDisplayString(formatTokens(summary.value.total_limit)), 1)
]),
_: 1
@@ -393,13 +394,13 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_VTab, { value: "usage" }, {
default: _withCtx(() => [...(_cache[18] || (_cache[18] = [
default: _withCtx(() => [...(_cache[19] || (_cache[19] = [
_createTextVNode("用量", -1)
]))]),
_: 1
}),
_createVNode(_component_VTab, { value: "config" }, {
default: _withCtx(() => [...(_cache[19] || (_cache[19] = [
default: _withCtx(() => [...(_cache[20] || (_cache[20] = [
_createTextVNode("配置", -1)
]))]),
_: 1
@@ -422,7 +423,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTable, { density: "comfortable" }, {
default: _withCtx(() => [
_cache[21] || (_cache[21] = _createElementVNode("thead", null, [
_cache[22] || (_cache[22] = _createElementVNode("thead", null, [
_createElementVNode("tr", null, [
_createElementVNode("th", null, "优先级"),
_createElementVNode("th", null, "名称"),
@@ -475,7 +476,7 @@ return (_ctx, _cache) => {
]))
}), 128)),
(!providerRows.value.length)
? (_openBlock(), _createElementBlock("tr", _hoisted_8, [...(_cache[20] || (_cache[20] = [
? (_openBlock(), _createElementBlock("tr", _hoisted_8, [...(_cache[21] || (_cache[21] = [
_createElementVNode("td", {
colspan: "8",
class: "text-center text-medium-emphasis py-8"
@@ -501,7 +502,7 @@ return (_ctx, _cache) => {
variant: "tonal",
onClick: addProvider
}, {
default: _withCtx(() => [...(_cache[22] || (_cache[22] = [
default: _withCtx(() => [...(_cache[23] || (_cache[23] = [
_createTextVNode("新增", -1)
]))]),
_: 1
@@ -512,7 +513,7 @@ return (_ctx, _cache) => {
variant: "tonal",
onClick: resetAllUsage
}, {
default: _withCtx(() => [...(_cache[23] || (_cache[23] = [
default: _withCtx(() => [...(_cache[24] || (_cache[24] = [
_createTextVNode("重置用量", -1)
]))]),
_: 1
@@ -525,7 +526,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTable, { density: "comfortable" }, {
default: _withCtx(() => [
_cache[25] || (_cache[25] = _createElementVNode("thead", null, [
_cache[26] || (_cache[26] = _createElementVNode("thead", null, [
_createElementVNode("tr", null, [
_createElementVNode("th", null, "启用"),
_createElementVNode("th", null, "优先级"),
@@ -577,7 +578,7 @@ return (_ctx, _cache) => {
]))
}), 128)),
(!config.value.providers?.length)
? (_openBlock(), _createElementBlock("tr", _hoisted_12, [...(_cache[24] || (_cache[24] = [
? (_openBlock(), _createElementBlock("tr", _hoisted_12, [...(_cache[25] || (_cache[25] = [
_createElementVNode("td", {
colspan: "9",
class: "text-center text-medium-emphasis py-8"
@@ -599,7 +600,7 @@ return (_ctx, _cache) => {
}, 8, ["modelValue"]),
_createVNode(_component_VDialog, {
modelValue: showEditor.value,
"onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((showEditor).value = $event)),
"onUpdate:modelValue": _cache[14] || (_cache[14] = $event => ((showEditor).value = $event)),
"max-width": "760"
}, {
default: _withCtx(() => [
@@ -698,6 +699,17 @@ return (_ctx, _cache) => {
]),
_: 1
}),
_createVNode(_component_VCol, { cols: "12" }, {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.user_agent,
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((editedProvider.value.user_agent) = $event)),
label: "User-Agent",
variant: "outlined"
}, null, 8, ["modelValue"])
]),
_: 1
}),
_createVNode(_component_VCol, {
cols: "12",
md: "6"
@@ -705,7 +717,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.token_limit,
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((editedProvider.value.token_limit) = $event)),
"onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((editedProvider.value.token_limit) = $event)),
modelModifiers: { number: true },
label: "Token 额度",
type: "number",
@@ -721,7 +733,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.used_tokens,
"onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((editedProvider.value.used_tokens) = $event)),
"onUpdate:modelValue": _cache[12] || (_cache[12] = $event => ((editedProvider.value.used_tokens) = $event)),
modelModifiers: { number: true },
label: "初始已用",
type: "number",
@@ -741,9 +753,9 @@ return (_ctx, _cache) => {
_createVNode(_component_VSpacer),
_createVNode(_component_VBtn, {
variant: "text",
onClick: _cache[12] || (_cache[12] = $event => (showEditor.value = false))
onClick: _cache[13] || (_cache[13] = $event => (showEditor.value = false))
}, {
default: _withCtx(() => [...(_cache[26] || (_cache[26] = [
default: _withCtx(() => [...(_cache[27] || (_cache[27] = [
_createTextVNode("取消", -1)
]))]),
_: 1
@@ -752,7 +764,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: commitProvider
}, {
default: _withCtx(() => [...(_cache[27] || (_cache[27] = [
default: _withCtx(() => [...(_cache[28] || (_cache[28] = [
_createTextVNode("确定", -1)
]))]),
_: 1
@@ -771,6 +783,6 @@ return (_ctx, _cache) => {
}
};
const AppPage = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-ff97075d"]]);
const AppPage = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-a0b44b7a"]]);
export { AppPage as default };

View File

@@ -52,6 +52,7 @@ function createProvider() {
provider: 'openai',
base_url: '',
api_key: '',
user_agent: '',
model: '',
token_limit: 0,
used_tokens: 0,
@@ -149,7 +150,7 @@ return (_ctx, _cache) => {
color: "transparent"
}, {
default: _withCtx(() => [
_cache[14] || (_cache[14] = _createElementVNode("div", { class: "text-h6 ms-3" }, "Agent Tokens 配置", -1)),
_cache[15] || (_cache[15] = _createElementVNode("div", { class: "text-h6 ms-3" }, "Agent Tokens 配置", -1)),
_createVNode(_component_VSpacer),
_createVNode(_component_VBtn, {
icon: "mdi-close",
@@ -184,7 +185,7 @@ return (_ctx, _cache) => {
variant: "tonal",
onClick: _cache[3] || (_cache[3] = $event => (emit('switch')))
}, {
default: _withCtx(() => [...(_cache[15] || (_cache[15] = [
default: _withCtx(() => [...(_cache[16] || (_cache[16] = [
_createTextVNode("用量", -1)
]))]),
_: 1
@@ -195,7 +196,7 @@ return (_ctx, _cache) => {
variant: "tonal",
onClick: addProvider
}, {
default: _withCtx(() => [...(_cache[16] || (_cache[16] = [
default: _withCtx(() => [...(_cache[17] || (_cache[17] = [
_createTextVNode("新增", -1)
]))]),
_: 1
@@ -208,7 +209,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTable, { density: "comfortable" }, {
default: _withCtx(() => [
_cache[18] || (_cache[18] = _createElementVNode("thead", null, [
_cache[19] || (_cache[19] = _createElementVNode("thead", null, [
_createElementVNode("tr", null, [
_createElementVNode("th", null, "启用"),
_createElementVNode("th", null, "优先级"),
@@ -256,7 +257,7 @@ return (_ctx, _cache) => {
]))
}), 128)),
(!localConfig.value.providers.length)
? (_openBlock(), _createElementBlock("tr", _hoisted_5, [...(_cache[17] || (_cache[17] = [
? (_openBlock(), _createElementBlock("tr", _hoisted_5, [...(_cache[18] || (_cache[18] = [
_createElementVNode("td", {
colspan: "7",
class: "text-center text-medium-emphasis py-8"
@@ -278,7 +279,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: saveConfig
}, {
default: _withCtx(() => [...(_cache[19] || (_cache[19] = [
default: _withCtx(() => [...(_cache[20] || (_cache[20] = [
_createTextVNode("保存", -1)
]))]),
_: 1
@@ -286,7 +287,7 @@ return (_ctx, _cache) => {
]),
_createVNode(_component_VDialog, {
modelValue: showEditor.value,
"onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((showEditor).value = $event)),
"onUpdate:modelValue": _cache[14] || (_cache[14] = $event => ((showEditor).value = $event)),
"max-width": "760"
}, {
default: _withCtx(() => [
@@ -385,6 +386,17 @@ return (_ctx, _cache) => {
]),
_: 1
}),
_createVNode(_component_VCol, { cols: "12" }, {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.user_agent,
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((editedProvider.value.user_agent) = $event)),
label: "User-Agent",
variant: "outlined"
}, null, 8, ["modelValue"])
]),
_: 1
}),
_createVNode(_component_VCol, {
cols: "12",
md: "6"
@@ -392,7 +404,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.token_limit,
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((editedProvider.value.token_limit) = $event)),
"onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((editedProvider.value.token_limit) = $event)),
modelModifiers: { number: true },
label: "Token 额度",
type: "number",
@@ -408,7 +420,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_VTextField, {
modelValue: editedProvider.value.used_tokens,
"onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((editedProvider.value.used_tokens) = $event)),
"onUpdate:modelValue": _cache[12] || (_cache[12] = $event => ((editedProvider.value.used_tokens) = $event)),
modelModifiers: { number: true },
label: "初始已用",
type: "number",
@@ -428,9 +440,9 @@ return (_ctx, _cache) => {
_createVNode(_component_VSpacer),
_createVNode(_component_VBtn, {
variant: "text",
onClick: _cache[12] || (_cache[12] = $event => (showEditor.value = false))
onClick: _cache[13] || (_cache[13] = $event => (showEditor.value = false))
}, {
default: _withCtx(() => [...(_cache[20] || (_cache[20] = [
default: _withCtx(() => [...(_cache[21] || (_cache[21] = [
_createTextVNode("取消", -1)
]))]),
_: 1
@@ -439,7 +451,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: commitProvider
}, {
default: _withCtx(() => [...(_cache[21] || (_cache[21] = [
default: _withCtx(() => [...(_cache[22] || (_cache[22] = [
_createTextVNode("确定", -1)
]))]),
_: 1
@@ -458,6 +470,6 @@ return (_ctx, _cache) => {
}
};
const Config = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-22b1ab53"]]);
const Config = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-ad13eb37"]]);
export { Config as default };

View File

@@ -1,4 +0,0 @@
.gap-2[data-v-22b1ab53] {
gap: 8px;
}

View File

@@ -0,0 +1,4 @@
.gap-2[data-v-ad13eb37] {
gap: 8px;
}

View File

@@ -1,5 +1,5 @@
import { importShared } from './__federation_fn_import-JrT3xvdd.js';
import AppPage from './__federation_expose_AppPage-DgQuvLJ5.js';
import AppPage from './__federation_expose_AppPage-D9YbltXb.js';
import { _ as _export_sfc } from './_plugin-vue_export-helper-pcqpp-6-.js';
const {createElementVNode:_createElementVNode,resolveComponent:_resolveComponent,createVNode:_createVNode,withCtx:_withCtx,openBlock:_openBlock,createElementBlock:_createElementBlock} = await importShared('vue');

View File

@@ -1,5 +1,5 @@
import { importShared } from './__federation_fn_import-JrT3xvdd.js';
import AppPage from './__federation_expose_AppPage-DgQuvLJ5.js';
import AppPage from './__federation_expose_AppPage-D9YbltXb.js';
true&&(function polyfill() {
const relList = document.createElement("link").relList;

View File

@@ -2,17 +2,17 @@ const currentImports = {};
const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
let moduleMap = {
"./Page":()=>{
dynamicLoadingCss(["__federation_expose_Page-vwwFlnk-.css","__federation_expose_AppPage-CkqsO_0n.css"], false, './Page');
return __federation_import('./__federation_expose_Page-HzVcafPU.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
dynamicLoadingCss(["__federation_expose_Page-vwwFlnk-.css","__federation_expose_AppPage-CoraCFRn.css"], false, './Page');
return __federation_import('./__federation_expose_Page-BrQfs0m0.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
"./Config":()=>{
dynamicLoadingCss(["__federation_expose_Config-C5yY6NwA.css"], false, './Config');
return __federation_import('./__federation_expose_Config-C7PQoNCG.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
dynamicLoadingCss(["__federation_expose_Config-Cgi4fBwN.css"], false, './Config');
return __federation_import('./__federation_expose_Config-Bkhux3_J.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
"./Dashboard":()=>{
dynamicLoadingCss([], false, './Dashboard');
return __federation_import('./__federation_expose_Dashboard-BPSul9jL.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
"./AppPage":()=>{
dynamicLoadingCss(["__federation_expose_AppPage-CkqsO_0n.css"], false, './AppPage');
return __federation_import('./__federation_expose_AppPage-DgQuvLJ5.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},};
dynamicLoadingCss(["__federation_expose_AppPage-CoraCFRn.css"], false, './AppPage');
return __federation_import('./__federation_expose_AppPage-D9YbltXb.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},};
const seen = {};
const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => {
const metaUrl = import.meta.url;

View File

@@ -1,6 +1,6 @@
<script type="module" crossorigin src="/assets/index-j_-8amFM.js"></script>
<script type="module" crossorigin src="/assets/index-DvGBSxrd.js"></script>
<link rel="modulepreload" crossorigin href="/assets/__federation_fn_import-JrT3xvdd.js">
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-pcqpp-6-.js">
<link rel="modulepreload" crossorigin href="/assets/__federation_expose_AppPage-DgQuvLJ5.js">
<link rel="stylesheet" crossorigin href="/assets/__federation_expose_AppPage-CkqsO_0n.css">
<link rel="modulepreload" crossorigin href="/assets/__federation_expose_AppPage-D9YbltXb.js">
<link rel="stylesheet" crossorigin href="/assets/__federation_expose_AppPage-CoraCFRn.css">
<div id="app"></div>

View File

@@ -1,7 +1,7 @@
{
"name": "moviepilot-agenttokens-plugin",
"private": true,
"version": "1.0.7",
"version": "1.0.8",
"type": "module",
"scripts": {
"build": "vite build"

View File

@@ -52,6 +52,7 @@ function createProvider() {
provider: 'openai',
base_url: '',
api_key: '',
user_agent: '',
model: '',
token_limit: 0,
used_tokens: 0,
@@ -365,6 +366,9 @@ onMounted(loadStatus)
<VCol cols="12">
<VTextField v-model="editedProvider.api_key" label="API Key" type="password" variant="outlined" />
</VCol>
<VCol cols="12">
<VTextField v-model="editedProvider.user_agent" label="User-Agent" variant="outlined" />
</VCol>
<VCol cols="12" md="6">
<VTextField v-model.number="editedProvider.token_limit" label="Token 额度" type="number" variant="outlined" />
</VCol>

View File

@@ -32,6 +32,7 @@ function createProvider() {
provider: 'openai',
base_url: '',
api_key: '',
user_agent: '',
model: '',
token_limit: 0,
used_tokens: 0,
@@ -188,6 +189,9 @@ onMounted(() => {
<VCol cols="12">
<VTextField v-model="editedProvider.api_key" label="API Key" type="password" variant="outlined" />
</VCol>
<VCol cols="12">
<VTextField v-model="editedProvider.user_agent" label="User-Agent" variant="outlined" />
</VCol>
<VCol cols="12" md="6">
<VTextField v-model.number="editedProvider.token_limit" label="Token 额度" type="number" variant="outlined" />
</VCol>

View File

@@ -3,7 +3,6 @@ import vue from '@vitejs/plugin-vue'
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
base: './',
plugins: [
vue(),
federation({
@@ -42,6 +41,16 @@ export default defineConfig({
},
},
},
{
postcssPlugin: 'vuetify-filter',
Root(root) {
root.walkRules(rule => {
if (rule.selector && (rule.selector.includes('.v-') || rule.selector.includes('.mdi-'))) {
rule.remove()
}
})
},
},
],
},
},