From 607c2296e337b0a5d5087da249f389429b9b3655 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 10:23:50 +0100 Subject: [PATCH] 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. --- docsgpt/connectors/catalog.py | 5 +- frontend/src/connectors/ActionParameters.tsx | 15 +++++ .../src/connectors/CredentialForm.test.tsx | 61 +++++++++++++++++++ frontend/src/connectors/CredentialForm.tsx | 59 +++++++++++++----- .../src/connectors/ToolPermissions.test.tsx | 20 ++++++ frontend/src/connectors/types.ts | 6 ++ frontend/src/locale/de.json | 9 ++- frontend/src/locale/en.json | 9 ++- frontend/src/locale/es.json | 9 ++- frontend/src/locale/jp.json | 9 ++- frontend/src/locale/ru.json | 9 ++- frontend/src/locale/zh-TW.json | 9 ++- frontend/src/locale/zh.json | 9 ++- 13 files changed, 198 insertions(+), 31 deletions(-) create mode 100644 frontend/src/connectors/CredentialForm.test.tsx diff --git a/docsgpt/connectors/catalog.py b/docsgpt/connectors/catalog.py index 54f0585a..e3a2d34d 100644 --- a/docsgpt/connectors/catalog.py +++ b/docsgpt/connectors/catalog.py @@ -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/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/getUpdates." ), ), ), diff --git a/frontend/src/connectors/ActionParameters.tsx b/frontend/src/connectors/ActionParameters.tsx index 8986bde9..b60e34d6 100644 --- a/frontend/src/connectors/ActionParameters.tsx +++ b/frontend/src/connectors/ActionParameters.tsx @@ -42,6 +42,21 @@ function ParameterRow({ } }; + if (parameter.set_by === 'account') + return ( +
  • +

    + {parameter.name} +

    +

    + {t('settings.connectors.parameters.fromAccount', { + value: String(parameter.value ?? ''), + interpolation: { escapeValue: false }, + })} +

    +
  • + ); + const choiceLabel = t('settings.connectors.parameters.choiceLabel', { parameter: parameter.name, interpolation: { escapeValue: false }, diff --git a/frontend/src/connectors/CredentialForm.test.tsx b/frontend/src/connectors/CredentialForm.test.tsx new file mode 100644 index 00000000..d74f6772 --- /dev/null +++ b/frontend/src/connectors/CredentialForm.test.tsx @@ -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( + undefined} + />, + ); + }); + const input = container.querySelector( + '#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'); + }); +}); diff --git a/frontend/src/connectors/CredentialForm.tsx b/frontend/src/connectors/CredentialForm.tsx index 94353c41..d568e4e3 100644 --- a/frontend/src/connectors/CredentialForm.tsx +++ b/frontend/src/connectors/CredentialForm.tsx @@ -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._`, then - * `settings.connectors.fields.`, then the catalog label. + * `settings.connectors.fields.`, then the catalog label; a field's hint + * from `settings.connectors.fieldHints._`, then the catalog. */ export default function CredentialForm({ connectorKey, @@ -28,23 +31,49 @@ export default function CredentialForm({ const { t } = useTranslation(); return (
    - {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) => + onChange({ ...values, [field.key]: e.target.value }), + }; + if (!field.hint) + return ( + + ); + return ( + + + + ); + })}
    ); } diff --git a/frontend/src/connectors/ToolPermissions.test.tsx b/frontend/src/connectors/ToolPermissions.test.tsx index e94a5ade..f7b85471 100644 --- a/frontend/src/connectors/ToolPermissions.test.tsx +++ b/frontend/src/connectors/ToolPermissions.test.tsx @@ -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( + '[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( diff --git a/frontend/src/connectors/types.ts b/frontend/src/connectors/types.ts index ed9cc6ae..10fe7e43 100644 --- a/frontend/src/connectors/types.ts +++ b/frontend/src/connectors/types.ts @@ -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 = { diff --git a/frontend/src/locale/de.json b/frontend/src/locale/de.json index 5e13a1c0..56aca398 100644 --- a/frontend/src/locale/de.json +++ b/frontend/src/locale/de.json @@ -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/getUpdates." } } }, diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 1cfd4e9c..904cc7cb 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -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/getUpdates." } } }, diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index 51ee876d..c69d4449 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -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/getUpdates." } } }, diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json index d271ecce..8d5aaae9 100644 --- a/frontend/src/locale/jp.json +++ b/frontend/src/locale/jp.json @@ -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/getUpdates でチャットの id を確認してください。" } } }, diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json index de52534d..13ae6189 100644 --- a/frontend/src/locale/ru.json +++ b/frontend/src/locale/ru.json @@ -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/getUpdates." } } }, diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json index 3b2903c5..c354531d 100644 --- a/frontend/src/locale/zh-TW.json +++ b/frontend/src/locale/zh-TW.json @@ -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/getUpdates 中找到該聊天的 id。" } } }, diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json index d22a13d7..887c7173 100644 --- a/frontend/src/locale/zh.json +++ b/frontend/src/locale/zh.json @@ -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/getUpdates 中找到该聊天的 id。" } } },