Realne sluzby, OpenAI, odesilani e-mailu a helpdesk

Katalog srovnany s tim, co opravdu bezi na services.csbot.cz/apps:
trinact sluzeb dostalo pristupove udaje a levne cteci overeni, opravena
appId, ktera nikam nevedla (ppl, microsoft365, transcription), a GA4,
Search Console, Google Ads i Sklik ted stoji na aplikaci analytics,
kazda s vlastnimi udaji. Nove sluzby SAP Business One, Google Workspace
a Meta Ads. K tomu 23 skriptu, ktere s nimi opravdu neco delaji.

OpenAI jako prvni sluzba, ktera nebezi u nas: Service.baseUrl s absolutni
adresou, prepis pres <SLUZBA>_BASE_URL nebo adresu u konektoru, predpona
hlavicky u pole udaju (uzivatel vlepi holy klic, Bearer dopise runtime).
Dotaz na model, nahrani souboru, otazka nad souborem, prepis zvuku.
Skript umi odeslat soubor pres ctx.http.postForm (multipart, obsah Base64).

Sluzba E-mail pres SMTP. Neni to skript, ale vnitrni krok - SMTP neni HTTP.
Konektor nese schranku firmy, krok ma HTML telo, ve kterem se dosazene
hodnoty escapuji (znacky autora sablony jsou zamer, ostre zavorky od
zakaznika ne). Overeni konektoru se prihlasi na server a nic neodesle.

Helpdesk: Ticket.helpdeskSourceId drzi firmu, ktera pozadavek poslala,
vlastnikem zustava ta, ktera ho resi - jinak by ho resitel nemel ve sve
fronte. Komu pozadavek pripadne, urcuje Tenant.helpdeskProviderId.
Zadavatel vidi jen svoje pozadavky a smi k nim pripsat komentar.

Opravy v portalu:
- hlasky o ulozisti a odchozi IP vidi jen spravce platformy
- typ ticketu se v automatizaci vybira ze seznamu firmy, nebo dosadi z dat
- stav ticketu je otevreny naseptavac, ne ciselnik
- ticket jde zalozit rucne, zakaznik u nej neni povinny
- kanal se prejmenoval a parametry u webhooku jsou oznacene jako nepovinne
- srovnane markdown tabulky v cele dokumentaci

Co z teto davky jeste neni: prepinac firmy je porad jen stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-08-26 07:40:16 +02:00
co-authored by Claude Opus 5
parent b25a149574
commit a771834e57
78 changed files with 7021 additions and 932 deletions
+112
View File
@@ -0,0 +1,112 @@
/**
* OpenAI: dotaz na jazykovy model.
*
* Sluzba: https://api.openai.com/v1 (nebezi u nas, adresa je v katalogu sluzeb)
* Endpoint: POST /chat/completions
*
* Model je **volny text s vychozi hodnotou**, ne vyber z pevneho seznamu.
* Seznam by zestarl pri kazdem vydani noveho modelu a krok stromu by pak
* odmital hodnotu, kterou ucet umi. Co ucet umi, vrati `openai.list-models`.
*/
export const manifest = {
id: 'openai.chat',
name: 'Zeptat se modelu',
description:
'Pošle otázku vybranému modelu OpenAI a vrátí odpověď včetně spotřeby tokenů. ' +
'Hodí se na shrnutí, klasifikaci i sepsání odpovědi zákazníkovi.',
inputs: [
{
id: 'model',
label: 'Model',
type: 'string',
required: false,
default: 'gpt-4o-mini',
hint: 'Například gpt-4o-mini, gpt-4o nebo gpt-4.1. Seznam vrátí operace Načíst seznam modelů.',
},
{
id: 'instructions',
label: 'Instrukce pro model',
type: 'string',
required: false,
multiline: true,
hint: 'Role a pravidla, například: Jsi zákaznická podpora, odpovídej česky a stručně.',
},
{
id: 'prompt',
label: 'Otázka',
type: 'string',
required: true,
multiline: true,
hint: 'Text dotazu. Může obsahovat data z předchozích kroků.',
},
{
id: 'temperature',
label: 'Teplota',
type: 'number',
required: false,
hint:
'Od 0 do 2. Nižší je předvídatelnější. Nevyplněné se neposílá vůbec, ' +
'protože novější modely teplotu nepřijímají.',
},
{
id: 'maxTokens',
label: 'Strop na délku odpovědi',
type: 'number',
required: false,
hint: 'V tokenech. Bez vyplnění rozhoduje model.',
},
{
id: 'jsonOutput',
label: 'Vyžádat odpověď v JSONu',
type: 'boolean',
required: false,
default: false,
hint: 'Model pak vrátí platný JSON. V instrukcích popište, jaká pole má mít.',
},
],
outputs: [
{ id: 'text', label: 'Odpověď', type: 'string', required: true },
{ id: 'model', label: 'Použitý model', type: 'string', required: true },
{ id: 'finishReason', label: 'Důvod ukončení', type: 'string', required: false },
{ id: 'promptTokens', label: 'Tokeny na vstupu', type: 'number', required: false },
{ id: 'completionTokens', label: 'Tokeny na výstupu', type: 'number', required: false },
{ id: 'totalTokens', label: 'Tokeny celkem', type: 'number', required: false },
],
// Delsi odpoved trva. Vychozich 15 s je na generovani textu malo.
timeoutMs: 60000,
};
export async function run(inputs, ctx) {
const { pick, text, num, need } = ctx.util;
const messages = [];
if (inputs.instructions) messages.push({ role: 'system', content: inputs.instructions });
messages.push({ role: 'user', content: inputs.prompt });
const body = { model: inputs.model, messages };
// Nevyplnene se neposila. Novejsi modely `temperature` odmitaji uplne,
// takze poslat vychozi hodnotu by krok rozbilo tam, kde o ni nikdo nestal.
if (inputs.temperature !== null) body.temperature = inputs.temperature;
if (inputs.maxTokens !== null) body.max_completion_tokens = inputs.maxTokens;
if (inputs.jsonOutput) body.response_format = { type: 'json_object' };
const { body: answer } = await ctx.http.post('/chat/completions', body);
const choice = ctx.util.first(pick(answer, 'choices'));
if (!choice) ctx.fail('OpenAI nevrátilo žádnou odpověď.', answer);
const usage = pick(answer, 'usage') ?? {};
return {
text: need(text(pick(pick(choice, 'message'), 'content')), 'odpověď modelu'),
model: need(text(pick(answer, 'model')), 'název modelu'),
finishReason: text(pick(choice, 'finish_reason')),
promptTokens: num(pick(usage, 'prompt_tokens')),
completionTokens: num(pick(usage, 'completion_tokens')),
totalTokens: num(pick(usage, 'total_tokens')),
};
}