mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 02:15:06 +00:00
Show the Telegram default chat in the connect form and the drawer
The connect form shows field hints; Telegram's Default chat ID says what it does and how to find a chat's id. In the drawer, a chat set on the account shows as set there instead of as a choice.
This commit is contained in:
1 parent
c3e83ff9dd
commit
607c2296e3
13 files changed
+198
-31
No files matched your search
@@ -316,8 +316,9 @@ _BUILT_IN: tuple[ConnectorDefinition, ...] = (
|
||||
required=False,
|
||||
parameter="chat_id",
|
||||
hint=(
|
||||
"Messages go to this chat, and the AI cannot pick another. Add the bot to the chat, send "
|
||||
"it a message, then find the chat's id in https://api.telegram.org/bot<token>/getUpdates."
|
||||
"Optional. Messages go to this chat, and the AI cannot pick another. Add the bot to the "
|
||||
"chat, send it a message, then find the chat's id in "
|
||||
"https://api.telegram.org/bot<token>/getUpdates."
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,6 +42,21 @@ function ParameterRow({
|
||||
}
|
||||
};
|
||||
|
||||
if (parameter.set_by === 'account')
|
||||
return (
|
||||
<li data-parameter={parameter.name} className="flex flex-col">
|
||||
<p className="text-foreground font-mono text-xs wrap-anywhere">
|
||||
{parameter.name}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-xs wrap-anywhere">
|
||||
{t('settings.connectors.parameters.fromAccount', {
|
||||
value: String(parameter.value ?? ''),
|
||||
interpolation: { escapeValue: false },
|
||||
})}
|
||||
</p>
|
||||
</li>
|
||||
);
|
||||
|
||||
const choiceLabel = t('settings.connectors.parameters.choiceLabel', {
|
||||
parameter: parameter.name,
|
||||
interpolation: { escapeValue: false },
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { act } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, opts?: { defaultValue?: string }) =>
|
||||
opts?.defaultValue ?? key,
|
||||
}),
|
||||
}));
|
||||
|
||||
import CredentialForm from './CredentialForm';
|
||||
|
||||
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
|
||||
|
||||
describe('CredentialForm', () => {
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await act(async () => root.unmount());
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it('shows a field hint under the field and links it to the input', async () => {
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<CredentialForm
|
||||
connectorKey="telegram"
|
||||
idPrefix="connect-telegram"
|
||||
fields={[
|
||||
{ key: 'token', label: 'Bot token', secret: true, required: true },
|
||||
{
|
||||
key: 'chat_id',
|
||||
label: 'Default chat ID',
|
||||
secret: false,
|
||||
required: false,
|
||||
hint: 'Messages go to this chat.',
|
||||
},
|
||||
]}
|
||||
values={{}}
|
||||
onChange={() => undefined}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
const input = container.querySelector<HTMLInputElement>(
|
||||
'#connect-telegram-chat_id',
|
||||
)!;
|
||||
expect(input.type).toBe('text');
|
||||
const hintId = input.getAttribute('aria-describedby')!;
|
||||
expect(document.getElementById(hintId)?.textContent).toBe(
|
||||
'Messages go to this chat.',
|
||||
);
|
||||
expect(container.textContent).toContain('Default chat ID');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { FormField } from '../components/ui/form-field';
|
||||
import { Input } from '../components/ui/input';
|
||||
import type { CredentialField } from './types';
|
||||
|
||||
@@ -7,7 +9,8 @@ import type { CredentialField } from './types';
|
||||
* The short form an API-key connector asks for, generated from its catalog
|
||||
* `credential_fields` (or `setup_fields`). Secrets are masked. Labels come
|
||||
* from `settings.connectors.fields.<connector>_<key>`, then
|
||||
* `settings.connectors.fields.<key>`, then the catalog label.
|
||||
* `settings.connectors.fields.<key>`, then the catalog label; a field's hint
|
||||
* from `settings.connectors.fieldHints.<connector>_<key>`, then the catalog.
|
||||
*/
|
||||
export default function CredentialForm({
|
||||
connectorKey,
|
||||
@@ -28,23 +31,49 @@ export default function CredentialForm({
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
{fields.map((field) => (
|
||||
<Input
|
||||
key={field.key}
|
||||
id={`${idPrefix}-${field.key}`}
|
||||
label={t(`settings.connectors.fields.${connectorKey}_${field.key}`, {
|
||||
{fields.map((field) => {
|
||||
const id = `${idPrefix}-${field.key}`;
|
||||
const label = t(
|
||||
`settings.connectors.fields.${connectorKey}_${field.key}`,
|
||||
{
|
||||
defaultValue: t(`settings.connectors.fields.${field.key}`, {
|
||||
defaultValue: field.label,
|
||||
}),
|
||||
})}
|
||||
labelSurface={labelSurface}
|
||||
type={field.secret ? 'password' : 'text'}
|
||||
autoComplete={field.secret ? 'new-password' : 'off'}
|
||||
required={field.required}
|
||||
value={values[field.key] ?? ''}
|
||||
onChange={(e) => onChange({ ...values, [field.key]: e.target.value })}
|
||||
/>
|
||||
))}
|
||||
},
|
||||
);
|
||||
const inputProps = {
|
||||
id,
|
||||
type: field.secret ? 'password' : 'text',
|
||||
autoComplete: field.secret ? 'new-password' : 'off',
|
||||
value: values[field.key] ?? '',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) =>
|
||||
onChange({ ...values, [field.key]: e.target.value }),
|
||||
};
|
||||
if (!field.hint)
|
||||
return (
|
||||
<Input
|
||||
key={field.key}
|
||||
label={label}
|
||||
labelSurface={labelSurface}
|
||||
required={field.required}
|
||||
{...inputProps}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<FormField
|
||||
key={field.key}
|
||||
label={label}
|
||||
required={field.required}
|
||||
labelSurface={labelSurface}
|
||||
hint={t(
|
||||
`settings.connectors.fieldHints.${connectorKey}_${field.key}`,
|
||||
{ defaultValue: field.hint },
|
||||
)}
|
||||
>
|
||||
<Input {...inputProps} />
|
||||
</FormField>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -243,6 +243,26 @@ describe('ToolPermissions', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('shows a chat the account sets as set there, not as a choice', async () => {
|
||||
await render(
|
||||
tool([
|
||||
action('telegram_send_message', 'write', 'ask', [
|
||||
{ ...param('chat_id', true, '-100'), set_by: 'account' },
|
||||
]),
|
||||
]),
|
||||
);
|
||||
await act(async () =>
|
||||
button('settings.connectors.parameters.show').click(),
|
||||
);
|
||||
const row = container.querySelector<HTMLElement>(
|
||||
'[data-parameter="chat_id"]',
|
||||
)!;
|
||||
expect(row.textContent).toContain(
|
||||
'settings.connectors.parameters.fromAccount',
|
||||
);
|
||||
expect(row.querySelector('[data-mode]')).toBeNull();
|
||||
});
|
||||
|
||||
it('says what Ask first means outside chat', async () => {
|
||||
await render(tool([action('create_issue', 'write', 'ask')]));
|
||||
expect(container.textContent).toContain(
|
||||
|
||||
@@ -33,6 +33,10 @@ export type CredentialField = {
|
||||
label: string;
|
||||
secret: boolean;
|
||||
required: boolean;
|
||||
/** A tool parameter this field sets for every call (Telegram's chat). */
|
||||
parameter?: string | null;
|
||||
/** English help under the field; translated when the locale has it. */
|
||||
hint?: string | null;
|
||||
};
|
||||
|
||||
export type ConnectorDefinition = {
|
||||
@@ -100,6 +104,8 @@ export type ActionParameter = {
|
||||
/** Sent with `value` on every call; the AI never sees it. */
|
||||
fixed: boolean;
|
||||
value: string | number | boolean | null;
|
||||
/** Who fixed it: the tool's own setting, or the account (a default chat). */
|
||||
set_by?: 'tool' | 'account' | null;
|
||||
};
|
||||
|
||||
export type ConnectionToolAction = {
|
||||
|
||||
@@ -1242,7 +1242,8 @@
|
||||
"postgres_token": "Verbindungszeichenfolge",
|
||||
"brave_token": "API-Schlüssel",
|
||||
"github_access_token": "Persönliches Zugriffstoken",
|
||||
"repo_url": "Repository"
|
||||
"repo_url": "Repository",
|
||||
"telegram_chat_id": "Standard-Chat-ID"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "{{name}} verbinden",
|
||||
@@ -1356,7 +1357,11 @@
|
||||
"valueLabel": "Wert für {{parameter}}",
|
||||
"save": "Speichern",
|
||||
"saveFailed": "Der Wert konnte nicht gespeichert werden. Versuche es erneut.",
|
||||
"hint": "Ein Wert mit „Immer verwenden“ wird bei jedem Aufruf gesendet. Die KI sieht ihn nicht und kann ihn nicht ändern."
|
||||
"hint": "Ein Wert mit „Immer verwenden“ wird bei jedem Aufruf gesendet. Die KI sieht ihn nicht und kann ihn nicht ändern.",
|
||||
"fromAccount": "In diesem Konto festgelegt: {{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "Optional. Nachrichten gehen an diesen Chat, und die KI kann keinen anderen wählen. Füge den Bot zum Chat hinzu, sende ihm eine Nachricht und finde die ID des Chats unter https://api.telegram.org/bot<token>/getUpdates."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1248,7 +1248,8 @@
|
||||
"postgres_token": "Connection string",
|
||||
"brave_token": "API key",
|
||||
"github_access_token": "Personal access token",
|
||||
"repo_url": "Repository"
|
||||
"repo_url": "Repository",
|
||||
"telegram_chat_id": "Default chat ID"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "Connect {{name}}",
|
||||
@@ -1362,7 +1363,11 @@
|
||||
"valueLabel": "Value for {{parameter}}",
|
||||
"save": "Save",
|
||||
"saveFailed": "Could not save the value. Try again.",
|
||||
"hint": "A value set to Always use is sent on every call. The AI never sees it and cannot change it."
|
||||
"hint": "A value set to Always use is sent on every call. The AI never sees it and cannot change it.",
|
||||
"fromAccount": "Set on this account: {{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "Optional. Messages go to this chat, and the AI cannot pick another. Add the bot to the chat, send it a message, then find the chat's id in https://api.telegram.org/bot<token>/getUpdates."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1242,7 +1242,8 @@
|
||||
"postgres_token": "Cadena de conexión",
|
||||
"brave_token": "Clave de API",
|
||||
"github_access_token": "Token de acceso personal",
|
||||
"repo_url": "Repositorio"
|
||||
"repo_url": "Repositorio",
|
||||
"telegram_chat_id": "ID del chat predeterminado"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "Conectar {{name}}",
|
||||
@@ -1356,7 +1357,11 @@
|
||||
"valueLabel": "Valor de {{parameter}}",
|
||||
"save": "Guardar",
|
||||
"saveFailed": "No se pudo guardar el valor. Inténtalo de nuevo.",
|
||||
"hint": "Un valor con «Usar siempre» se envía en cada llamada. La IA nunca lo ve ni puede cambiarlo."
|
||||
"hint": "Un valor con «Usar siempre» se envía en cada llamada. La IA nunca lo ve ni puede cambiarlo.",
|
||||
"fromAccount": "Definido en esta cuenta: {{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "Opcional. Los mensajes van a este chat y la IA no puede elegir otro. Añade el bot al chat, envíale un mensaje y busca el id del chat en https://api.telegram.org/bot<token>/getUpdates."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1230,7 +1230,8 @@
|
||||
"postgres_token": "接続文字列",
|
||||
"brave_token": "API キー",
|
||||
"github_access_token": "個人用アクセストークン",
|
||||
"repo_url": "リポジトリ"
|
||||
"repo_url": "リポジトリ",
|
||||
"telegram_chat_id": "既定のチャット ID"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "{{name}} に接続",
|
||||
@@ -1337,7 +1338,11 @@
|
||||
"valueLabel": "{{parameter}} の値",
|
||||
"save": "保存",
|
||||
"saveFailed": "値を保存できませんでした。もう一度お試しください。",
|
||||
"hint": "「常に使用」の値は毎回の呼び出しで送信されます。AI はこの値を見ることも変更することもできません。"
|
||||
"hint": "「常に使用」の値は毎回の呼び出しで送信されます。AI はこの値を見ることも変更することもできません。",
|
||||
"fromAccount": "このアカウントで設定済み: {{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "任意。メッセージはこのチャットに送られ、AI は別のチャットを選べません。ボットをチャットに追加してメッセージを送り、https://api.telegram.org/bot<token>/getUpdates でチャットの id を確認してください。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1304,7 +1304,8 @@
|
||||
"postgres_token": "Строка подключения",
|
||||
"brave_token": "API-ключ",
|
||||
"github_access_token": "Персональный токен доступа",
|
||||
"repo_url": "Репозиторий"
|
||||
"repo_url": "Репозиторий",
|
||||
"telegram_chat_id": "ID чата по умолчанию"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "Подключить {{name}}",
|
||||
@@ -1432,7 +1433,11 @@
|
||||
"valueLabel": "Значение {{parameter}}",
|
||||
"save": "Сохранить",
|
||||
"saveFailed": "Не удалось сохранить значение. Попробуйте ещё раз.",
|
||||
"hint": "Значение «Всегда использовать» отправляется при каждом вызове. ИИ его не видит и не может изменить."
|
||||
"hint": "Значение «Всегда использовать» отправляется при каждом вызове. ИИ его не видит и не может изменить.",
|
||||
"fromAccount": "Задано в этом аккаунте: {{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "Необязательно. Сообщения уходят в этот чат, и ИИ не может выбрать другой. Добавьте бота в чат, отправьте ему сообщение и найдите id чата на https://api.telegram.org/bot<token>/getUpdates."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1230,7 +1230,8 @@
|
||||
"postgres_token": "連線字串",
|
||||
"brave_token": "API 金鑰",
|
||||
"github_access_token": "個人存取權杖",
|
||||
"repo_url": "儲存庫"
|
||||
"repo_url": "儲存庫",
|
||||
"telegram_chat_id": "預設聊天 ID"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "連線 {{name}}",
|
||||
@@ -1337,7 +1338,11 @@
|
||||
"valueLabel": "{{parameter}} 的值",
|
||||
"save": "儲存",
|
||||
"saveFailed": "無法儲存此值。請再試一次。",
|
||||
"hint": "設為「一律使用」的值會在每次呼叫時送出。AI 看不到它,也無法變更。"
|
||||
"hint": "設為「一律使用」的值會在每次呼叫時送出。AI 看不到它,也無法變更。",
|
||||
"fromAccount": "已在此帳戶中設定:{{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "選填。訊息會傳送到此聊天,AI 無法選擇其他聊天。將機器人加入聊天並傳送一則訊息給它,然後在 https://api.telegram.org/bot<token>/getUpdates 中找到該聊天的 id。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1230,7 +1230,8 @@
|
||||
"postgres_token": "连接字符串",
|
||||
"brave_token": "API 密钥",
|
||||
"github_access_token": "个人访问令牌",
|
||||
"repo_url": "仓库"
|
||||
"repo_url": "仓库",
|
||||
"telegram_chat_id": "默认聊天 ID"
|
||||
},
|
||||
"wizard": {
|
||||
"connectTitle": "连接 {{name}}",
|
||||
@@ -1337,7 +1338,11 @@
|
||||
"valueLabel": "{{parameter}} 的值",
|
||||
"save": "保存",
|
||||
"saveFailed": "无法保存该值。请重试。",
|
||||
"hint": "设为“始终使用”的值会在每次调用时发送。AI 看不到它,也无法更改。"
|
||||
"hint": "设为“始终使用”的值会在每次调用时发送。AI 看不到它,也无法更改。",
|
||||
"fromAccount": "已在此账户中设置:{{value}}"
|
||||
},
|
||||
"fieldHints": {
|
||||
"telegram_chat_id": "可选。消息会发送到此聊天,AI 无法选择其他聊天。将机器人加入聊天并给它发一条消息,然后在 https://api.telegram.org/bot<token>/getUpdates 中找到该聊天的 id。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user