AI zpracovani textu funguje: klic, adresa a tri skripty
Sluzba byla v katalogu bez pristupovych udaju a s appId 'ai-text', tedy mirila na aplikaci, ktera neexistuje. Konektor nemel co vyplnit a krok nemel co vykonat. - sluzba stoji na api.openai.com/v1 stejne jako OpenAI, overeni GET /models - model se bere z konektoru (ctx.config.model), ne z kazdeho kroku zvlast - skripty ai-text.classify, ai-text.extract, ai-text.generate Proc to neni jedna sluzba s OpenAI: tam se posila volny dotaz a vraci volny text. Tady je uloha pevna a vystup taky - category a confidence, objekt s popsanymi poli, text s poctem slov. Na to jde ve strome navazat podminkou, na volny text ne. Zjisteno u toho a zapsane do 21-realne-sluzby.md: proxy vraci na neznamou aplikaci 200 s prazdnym telem, takze overeni konektoru u sluzby bez verifyPath projde i tam, kde nic nebezi. Zatim neopraveno. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d71c111cf3
commit
aa0a783095
@@ -18,23 +18,24 @@ aplikaci nemaji.
|
||||
|
||||
## Ktera sluzba stoji na cem
|
||||
|
||||
| Sluzba katalogu | Aplikace (`appId`) | Overeni (`verifyPath`) |
|
||||
| ------------------ | ----------------------------- | ---------------------------------------------- |
|
||||
| iDoklad | `idoklad` | `/account/agenda` |
|
||||
| RAYNET CRM | `raynet` | `/company?limit=1` |
|
||||
| CSOB (PSD2) | `csob` | `/accounts?size=1` |
|
||||
| SAP Business One | `sap-bo` | `/api/system/info` |
|
||||
| PPL CPL | `pplcplapi` | `/customer` |
|
||||
| Microsoft 365 | `microsoft-365-service` | `/status` |
|
||||
| Google Workspace | `google-service` | `/google/drive/files` |
|
||||
| Google Analytics 4 | `analytics` | `/ga/admin/accountSummaries` |
|
||||
| Search Console | `analytics` | `/gsc/sites` |
|
||||
| Google Ads | `analytics` | `/googleads/customers:listAccessibleCustomers` |
|
||||
| Sklik | `analytics` | `/sklik/limits` |
|
||||
| Meta Ads | `meta` | `/ads/me/adaccounts` |
|
||||
| Prepis hovoru | `audio-transcription` | nema, overi se jen dostupnost |
|
||||
| E-mail | SMTP server firmy | prihlaseni na server, nic se neodesila |
|
||||
| OpenAI | mimo nas, `api.openai.com/v1` | `/models` |
|
||||
| Sluzba katalogu | Aplikace (`appId`) | Overeni (`verifyPath`) |
|
||||
| ------------------- | ----------------------------- | ---------------------------------------------- |
|
||||
| iDoklad | `idoklad` | `/account/agenda` |
|
||||
| RAYNET CRM | `raynet` | `/company?limit=1` |
|
||||
| CSOB (PSD2) | `csob` | `/accounts?size=1` |
|
||||
| SAP Business One | `sap-bo` | `/api/system/info` |
|
||||
| PPL CPL | `pplcplapi` | `/customer` |
|
||||
| Microsoft 365 | `microsoft-365-service` | `/status` |
|
||||
| Google Workspace | `google-service` | `/google/drive/files` |
|
||||
| Google Analytics 4 | `analytics` | `/ga/admin/accountSummaries` |
|
||||
| Search Console | `analytics` | `/gsc/sites` |
|
||||
| Google Ads | `analytics` | `/googleads/customers:listAccessibleCustomers` |
|
||||
| Sklik | `analytics` | `/sklik/limits` |
|
||||
| Meta Ads | `meta` | `/ads/me/adaccounts` |
|
||||
| Prepis hovoru | `audio-transcription` | nema, overi se jen dostupnost |
|
||||
| E-mail | SMTP server firmy | prihlaseni na server, nic se neodesila |
|
||||
| OpenAI | mimo nas, `api.openai.com/v1` | `/models` |
|
||||
| AI zpracovani textu | mimo nas, `api.openai.com/v1` | `/models` |
|
||||
|
||||
**Ctyri sluzby na jedne aplikaci.** GA4, Search Console, Google Ads a Sklik
|
||||
bezi v `analytics`, ale kazda ma **jine pristupove udaje** a jine ceny za
|
||||
@@ -47,9 +48,23 @@ overene nejsou - test, ktery projde i se spatnym klicem, by lhal.
|
||||
|
||||
## Sluzby, ktere aplikaci zatim nemaji
|
||||
|
||||
E-shop, WhatsApp, Facebook Messenger, Instagram, SMS, Slack, Voicebot
|
||||
a AI zpracovani textu jsou v katalogu jako popis toho, co chceme umet. Konektor
|
||||
u nich zalozit jde, ale overeni skonci chybou, protoze na te adrese nic nebezi.
|
||||
E-shop, WhatsApp, Facebook Messenger, Instagram, SMS a Slack jsou v katalogu
|
||||
jako popis toho, co chceme umet. Konektor u nich zalozit jde, ale nic za nim
|
||||
neni.
|
||||
|
||||
**Pozor: overeni u nich projde.** Proxy vraci na neznamou aplikaci `200`
|
||||
s prazdnym telem, takze `GET /apps/<neco>/health` vypada jako zdrava sluzba
|
||||
i u aplikace, ktera neexistuje:
|
||||
|
||||
```bash
|
||||
curl -o /dev/null -w "%{http_code}" https://services.csbot.cz/apps/tohle-neexistuje-xyz/health
|
||||
# 200
|
||||
```
|
||||
|
||||
Sluzba bez `verifyPath` se overuje prave pres `/health`, takze konektor rekne
|
||||
"Sluzba odpovida" i tam, kde nic nebezi. Neni to chyba overovani, je to chyba
|
||||
predpokladu: 2xx od proxy neznamena, ze za ni neco je. Napravit to znamena
|
||||
vyzadovat, aby `/health` neco odpovedel, ne jen vratil kod.
|
||||
|
||||
Meta Ads je **neco jineho nez Facebook Messenger a Instagram**: aplikace `meta`
|
||||
je nad Marketing API, tedy reklamy, a je jen pro cteni. Zpravy ze stranky
|
||||
@@ -130,9 +145,26 @@ server-to-server nehodi. App secret je povinny tam, kde ma aplikace zapnute
|
||||
Deepgram i OpenAI klic. Obe sluzby bezi paralelne a treti volani jejich
|
||||
vysledky slucuje, takze bez obou klicu to nefunguje.
|
||||
|
||||
### OpenAI
|
||||
### OpenAI a AI zpracovani textu
|
||||
|
||||
Jen API klic. Zadava se **holy**, slovo `Bearer` dopise portal - viz nize.
|
||||
Obe stoji na temze API a obe chteji jen API klic. Zadava se **holy**, slovo
|
||||
`Bearer` dopise portal - viz nize.
|
||||
|
||||
Rozdil je v tom, na co se ptaji. **OpenAI** posila volny dotaz a vraci volny
|
||||
text; hodi se, kdyz clovek vi, co chce napsat. **AI zpracovani textu** ma
|
||||
hotove ulohy s pevnym vystupem:
|
||||
|
||||
| Operace | Vraci |
|
||||
| -------------------- | ----------------------------------------------- |
|
||||
| Zaradit do kategorie | `category`, `confidence` a jestli je ze seznamu |
|
||||
| Vytahnout udaje | objekt s poli, ktera si popsal uzivatel |
|
||||
| Vygenerovat text | text plus pocet slov |
|
||||
|
||||
Pevny vystup je ten duvod, proc to neni jedna sluzba: na `category`
|
||||
a `confidence` jde ve strome navazat podminkou, na volny text ne.
|
||||
|
||||
Model se u teto sluzby bere z konektoru (`ctx.config.model`), aby se nemusel
|
||||
vyplnovat u kazdeho kroku znovu.
|
||||
|
||||
## OpenAI: sluzba, ktera nebezi u nas
|
||||
|
||||
@@ -328,6 +360,9 @@ sluzeb: OpenAI zvladne stovky megabajtu, nas zaznam behu ne.
|
||||
| `openai.ask-about-file` | otazka nad souborem pres Responses API |
|
||||
| `openai.transcribe-audio` | prepis zvuku |
|
||||
| `openai.list-models` | co ucet umi |
|
||||
| `ai-text.classify` | zarazeni s jistotou, hlida vycet kategorii |
|
||||
| `ai-text.extract` | vystup podle poli, ktera popsal uzivatel |
|
||||
| `ai-text.generate` | text s omezenim delky, tonu a jazyka |
|
||||
|
||||
E-mail v tabulce neni schvalne: neni to skript, ale vnitrni krok, viz vyse.
|
||||
|
||||
|
||||
@@ -2,6 +2,36 @@
|
||||
|
||||
Nejnovejsi nahore.
|
||||
|
||||
## 2026-08-28 - AI zpracovani textu funguje
|
||||
|
||||
Sluzba byla v katalogu bez pristupovych udaju a s `appId: 'ai-text'`, tedy
|
||||
mirila na aplikaci, ktera neexistuje. Konektor nemel co vyplnit a krok nemel
|
||||
co vykonat.
|
||||
|
||||
### Zmeneno
|
||||
|
||||
- **Sluzba ma API klic a skutecnou adresu.** Stoji na `api.openai.com/v1`
|
||||
stejne jako OpenAI, overeni je `GET /models`. Model se bere z konektoru
|
||||
(`ctx.config.model`), aby se nevyplnoval u kazdeho kroku znovu.
|
||||
|
||||
### Pridano
|
||||
|
||||
- **Tri skripty**: `ai-text.classify`, `ai-text.extract`, `ai-text.generate`.
|
||||
|
||||
Proc to neni jedna sluzba s OpenAI: tam se posila volny dotaz a vraci volny
|
||||
text. Tady je uloha pevna a **vystup taky** - `category` a `confidence`,
|
||||
objekt s popsanymi poli, text s poctem slov. Prave na to jde ve strome navazat
|
||||
podminkou, na volny text ne.
|
||||
|
||||
### Zjisteno u toho
|
||||
|
||||
- **Overeni konektoru projde i u sluzby, ktera neexistuje.** Proxy vraci na
|
||||
neznamou aplikaci `200` s prazdnym telem, a sluzba bez `verifyPath` se
|
||||
overuje prave pres `/health`. Konektor tedy rekne "Sluzba odpovida" i tam,
|
||||
kde nic nebezi. Tyka se to E-shopu, WhatsAppu, Facebooku, Instagramu, SMS
|
||||
a Slacku. Zapsane v [21-realne-sluzby.md](21-realne-sluzby.md), zatim
|
||||
neopravene.
|
||||
|
||||
## 2026-08-26 - jazyky, skutecny snimek v heru a opravene titulky
|
||||
|
||||
### Pridano
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* AI zpracovani textu: zarazeni do kategorie.
|
||||
*
|
||||
* Sluzba: https://api.openai.com/v1 (nebezi u nas, adresa je v katalogu)
|
||||
* Endpoint: POST /chat/completions
|
||||
*
|
||||
* Rozdil proti sluzbe OpenAI: tam se posila volny dotaz a vraci se volny text.
|
||||
* Tady je uloha pevna a **vystup taky** - `category` a `confidence`. Prave na
|
||||
* to se da ve strome navazat podminkou, coz u volneho textu nejde.
|
||||
*
|
||||
* Model se bere z konektoru (`ctx.config.model`), aby se nemusel vyplnovat
|
||||
* u kazdeho kroku znovu.
|
||||
*/
|
||||
|
||||
export const manifest = {
|
||||
id: 'ai-text.classify',
|
||||
name: 'Zařadit do kategorie',
|
||||
description:
|
||||
'Rozhodne, do které z uvedených kategorií text patří. Vrací kategorii ' +
|
||||
'a jistotu, takže se za krokem dá větvit podle toho, jak si je model jistý.',
|
||||
|
||||
inputs: [
|
||||
{
|
||||
id: 'categories',
|
||||
label: 'Seznam kategorií',
|
||||
type: 'string',
|
||||
required: true,
|
||||
hint: 'Oddělte čárkou, například: porucha, dotaz, reklamace.',
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
type: 'string',
|
||||
required: true,
|
||||
multiline: true,
|
||||
hint: 'Co se má zařadit. Typicky předmět a tělo ticketu.',
|
||||
},
|
||||
{
|
||||
id: 'instructions',
|
||||
label: 'Doplňující pravidla',
|
||||
type: 'string',
|
||||
required: false,
|
||||
multiline: true,
|
||||
hint: 'Například: reklamací se rozumí jen stížnost na už dodané zboží.',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: [
|
||||
{ id: 'category', label: 'Kategorie', type: 'string', required: true },
|
||||
{ id: 'confidence', label: 'Jistota', type: 'number', required: true },
|
||||
{ id: 'matched', label: 'Kategorie ze seznamu', type: 'boolean', required: true },
|
||||
],
|
||||
|
||||
timeoutMs: 45000,
|
||||
};
|
||||
|
||||
export async function run(inputs, ctx) {
|
||||
const { pick, first, text, num, need } = ctx.util;
|
||||
|
||||
const categories = inputs.categories
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
if (categories.length < 2) ctx.fail('Uveďte aspoň dvě kategorie, jinak není z čeho vybírat.');
|
||||
|
||||
const system =
|
||||
'Jsi klasifikátor. Vrátíš POUZE platný JSON ve tvaru ' +
|
||||
'{"category": "...", "confidence": 0.0}. Pole category musí být přesně jedna ' +
|
||||
`z těchto hodnot: ${categories.join(', ')}. Confidence je číslo od 0 do 1.` +
|
||||
(inputs.instructions ? ` Další pravidla: ${inputs.instructions}` : '');
|
||||
|
||||
const { body } = await ctx.http.post('/chat/completions', {
|
||||
model: ctx.config.model || 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: system },
|
||||
{ role: 'user', content: inputs.text },
|
||||
],
|
||||
response_format: { type: 'json_object' },
|
||||
});
|
||||
|
||||
const content = text(pick(pick(first(pick(body, 'choices')), 'message'), 'content'));
|
||||
if (!content) ctx.fail('Model nevrátil odpověď.', body);
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch {
|
||||
ctx.fail(`Model nevrátil platný JSON: ${content}`);
|
||||
}
|
||||
|
||||
const category = need(text(pick(parsed, 'category')), 'kategorii');
|
||||
|
||||
/*
|
||||
* Model obcas vrati kategorii, ktera v seznamu neni, i kdyz ji ma zakazanou.
|
||||
* Neni to duvod krok shodit - vysledek se vrati a `matched` rekne, ze je
|
||||
* mimo seznam. Strom se pak muze rozhodnout, jestli mu verit.
|
||||
*/
|
||||
const matched = categories.some((item) => item.toLowerCase() === category.toLowerCase());
|
||||
if (!matched) ctx.log(`Model vrátil "${category}", což v seznamu není.`);
|
||||
|
||||
return {
|
||||
category,
|
||||
confidence: num(pick(parsed, 'confidence'), 0) ?? 0,
|
||||
matched,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* AI zpracovani textu: vytazeni udaju.
|
||||
*
|
||||
* Sluzba: https://api.openai.com/v1
|
||||
* Endpoint: POST /chat/completions
|
||||
*
|
||||
* Vzor **vystupu, ktery si urcuje uzivatel**. Pole se popisou v kroku stromu
|
||||
* a model je vrati jako objekt. Manifest je predem znat nemuze, proto se vraci
|
||||
* jeden parametr typu `object` a vedle nej priznak, co se opravdu naslo.
|
||||
*
|
||||
* Nenalezeny udaj je `null`, ne vymysleny. Model, ktery hada, je horsi nez
|
||||
* model, ktery rekne "nevim" - vymyslene IC se poznat neda.
|
||||
*/
|
||||
|
||||
export const manifest = {
|
||||
id: 'ai-text.extract',
|
||||
name: 'Vytáhnout údaje',
|
||||
description:
|
||||
'Z volného textu získá strukturovaná data podle polí, která popíšete. ' +
|
||||
'Co v textu není, vrátí prázdné, nedomýšlí si.',
|
||||
|
||||
inputs: [
|
||||
{
|
||||
id: 'fields',
|
||||
label: 'Pole k vytažení',
|
||||
type: 'string',
|
||||
required: true,
|
||||
multiline: true,
|
||||
hint:
|
||||
'Jedno pole na řádek ve tvaru nazev: popis. Například:\n' +
|
||||
'cisloObjednavky: číslo objednávky, obvykle ve tvaru OBJ-1234\n' +
|
||||
'castka: celková částka v korunách jako číslo',
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
type: 'string',
|
||||
required: true,
|
||||
multiline: true,
|
||||
},
|
||||
],
|
||||
|
||||
outputs: [
|
||||
{ id: 'data', label: 'Vytažené údaje', type: 'object', required: true },
|
||||
{ id: 'found', label: 'Kolik polí se našlo', type: 'number', required: true },
|
||||
{ id: 'missing', label: 'Co se nenašlo', type: 'string', required: false },
|
||||
{ id: 'complete', label: 'Našlo se všechno', type: 'boolean', required: true },
|
||||
],
|
||||
|
||||
timeoutMs: 45000,
|
||||
};
|
||||
|
||||
export async function run(inputs, ctx) {
|
||||
const { pick, first, text } = ctx.util;
|
||||
|
||||
const fields = inputs.fields
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const at = line.indexOf(':');
|
||||
return at === -1
|
||||
? { name: line, description: '' }
|
||||
: { name: line.slice(0, at).trim(), description: line.slice(at + 1).trim() };
|
||||
})
|
||||
.filter((field) => field.name !== '');
|
||||
|
||||
if (fields.length === 0) ctx.fail('Nepopsali jste žádné pole, takže není co vytáhnout.');
|
||||
|
||||
const wanted = fields.map((f) => `- ${f.name}${f.description ? `: ${f.description}` : ''}`);
|
||||
|
||||
const system =
|
||||
'Vytáhni z textu uvedená pole a vrať POUZE platný JSON objekt s přesně těmito klíči:\n' +
|
||||
`${wanted.join('\n')}\n` +
|
||||
'Co v textu není, vrať jako null. Nic si nedomýšlej a nic nedopočítávej.';
|
||||
|
||||
const { body } = await ctx.http.post('/chat/completions', {
|
||||
model: ctx.config.model || 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: system },
|
||||
{ role: 'user', content: inputs.text },
|
||||
],
|
||||
response_format: { type: 'json_object' },
|
||||
});
|
||||
|
||||
const content = text(pick(pick(first(pick(body, 'choices')), 'message'), 'content'));
|
||||
if (!content) ctx.fail('Model nevrátil odpověď.', body);
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch {
|
||||
ctx.fail(`Model nevrátil platný JSON: ${content}`);
|
||||
}
|
||||
|
||||
// Vysledek se sklada podle **popsanych poli**, ne podle toho, co model poslal.
|
||||
// Kdyby si pridal klic navic, strom by o nem nevedel a jen by mu zabiral misto.
|
||||
const data = {};
|
||||
const missing = [];
|
||||
for (const field of fields) {
|
||||
const value = parsed[field.name];
|
||||
data[field.name] = value === undefined ? null : value;
|
||||
if (value === undefined || value === null || value === '') missing.push(field.name);
|
||||
}
|
||||
|
||||
if (missing.length > 0) ctx.log(`Nenašlo se: ${missing.join(', ')}.`);
|
||||
|
||||
return {
|
||||
data,
|
||||
found: fields.length - missing.length,
|
||||
missing: missing.length > 0 ? missing.join(', ') : null,
|
||||
complete: missing.length === 0,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* AI zpracovani textu: napsani textu podle instrukce.
|
||||
*
|
||||
* Sluzba: https://api.openai.com/v1
|
||||
* Endpoint: POST /chat/completions
|
||||
*
|
||||
* Typicky se z toho stava odpoved zakaznikovi nebo shrnuti do ticketu, takze
|
||||
* krok umi omezit delku a jazyk. Bez toho model odpovi, jak se mu zachce,
|
||||
* a odpoved se do e-mailu nebo do komentare nevejde.
|
||||
*/
|
||||
|
||||
export const manifest = {
|
||||
id: 'ai-text.generate',
|
||||
name: 'Vygenerovat text',
|
||||
description:
|
||||
'Napíše text podle instrukce a vstupních dat. Hodí se na odpověď ' +
|
||||
'zákazníkovi, shrnutí do ticketu nebo popis incidentu.',
|
||||
|
||||
inputs: [
|
||||
{
|
||||
id: 'instruction',
|
||||
label: 'Co má napsat',
|
||||
type: 'string',
|
||||
required: true,
|
||||
multiline: true,
|
||||
hint: 'Například: Napiš zdvořilou odpověď zákazníkovi, že reklamaci přebíráme.',
|
||||
},
|
||||
{
|
||||
id: 'input',
|
||||
label: 'Vstupní data',
|
||||
type: 'string',
|
||||
required: false,
|
||||
multiline: true,
|
||||
hint: 'Text, ze kterého má vycházet. Typicky obsah ticketu.',
|
||||
},
|
||||
{
|
||||
id: 'tone',
|
||||
label: 'Tón',
|
||||
type: 'string',
|
||||
required: false,
|
||||
options: [
|
||||
{ value: 'vecny', label: 'Věcný' },
|
||||
{ value: 'vstricny', label: 'Vstřícný' },
|
||||
{ value: 'strucny', label: 'Stručný' },
|
||||
{ value: 'omluvny', label: 'Omluvný' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'language',
|
||||
label: 'Jazyk',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: 'čeština',
|
||||
hint: 'Bez vyplnění čeština.',
|
||||
},
|
||||
{
|
||||
id: 'maxWords',
|
||||
label: 'Nejvýše kolik slov',
|
||||
type: 'number',
|
||||
required: false,
|
||||
hint: 'Bez vyplnění rozhoduje model. Do e-mailu se hodí kolem 120.',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: [
|
||||
{ id: 'text', label: 'Text', type: 'string', required: true },
|
||||
{ id: 'words', label: 'Počet slov', type: 'number', required: true },
|
||||
],
|
||||
|
||||
timeoutMs: 60000,
|
||||
};
|
||||
|
||||
const tones = {
|
||||
vecny: 'Piš věcně a bez zdobení.',
|
||||
vstricny: 'Piš vstřícně a lidsky, ale ne přehnaně.',
|
||||
strucny: 'Piš co nejstručněji, ideálně pár vět.',
|
||||
omluvny: 'Omluv se za potíže, ale neslibuj nic, co není v zadání.',
|
||||
};
|
||||
|
||||
export async function run(inputs, ctx) {
|
||||
const { pick, first, text, need } = ctx.util;
|
||||
|
||||
const rules = [
|
||||
`Piš v jazyce: ${inputs.language}.`,
|
||||
inputs.tone ? tones[inputs.tone] : '',
|
||||
inputs.maxWords !== null ? `Vejdi se do ${inputs.maxWords} slov.` : '',
|
||||
// Bez tohohle model rad pripoji "Jiste, tady je vas text:" a to jde
|
||||
// rovnou zakaznikovi do e-mailu.
|
||||
'Vrať pouze výsledný text, žádný úvod ani komentář k zadání.',
|
||||
].filter(Boolean);
|
||||
|
||||
const { body } = await ctx.http.post('/chat/completions', {
|
||||
model: ctx.config.model || 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: rules.join(' ') },
|
||||
{
|
||||
role: 'user',
|
||||
content: inputs.input
|
||||
? `${inputs.instruction}\n\nVstupní data:\n${inputs.input}`
|
||||
: inputs.instruction,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const content = need(
|
||||
text(pick(pick(first(pick(body, 'choices')), 'message'), 'content')),
|
||||
'vygenerovaný text',
|
||||
);
|
||||
|
||||
return {
|
||||
text: content,
|
||||
words: content.split(/\s+/).filter(Boolean).length,
|
||||
};
|
||||
}
|
||||
+40
-2
@@ -2314,9 +2314,47 @@ export const services: Service[] = [
|
||||
icon: 'Sparkles',
|
||||
status: 'available',
|
||||
general: false,
|
||||
appId: 'ai-text',
|
||||
/*
|
||||
* Nebezi u nas, stejne jako OpenAI. Rozdil proti sluzbe OpenAI je v tom,
|
||||
* na co se pta: tam se posila volny dotaz, tady jsou hotove ulohy
|
||||
* (zaradit, vytahnout, napsat) s pevnym vystupem, na ktery se da ve strome
|
||||
* navazat podminkou.
|
||||
*/
|
||||
appId: null,
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
visibility: { mode: 'everyone', tenantIds: [], userIds: [] },
|
||||
credentials: [],
|
||||
credentials: [
|
||||
{
|
||||
id: 'apiKey',
|
||||
label: 'API klíč',
|
||||
target: 'header',
|
||||
name: 'Authorization',
|
||||
prefix: 'Bearer ',
|
||||
required: true,
|
||||
secret: true,
|
||||
hint: 'platform.openai.com, sekce API keys. Vložte jen klíč, slovo Bearer doplní portál.',
|
||||
},
|
||||
{
|
||||
id: 'model',
|
||||
label: 'Model',
|
||||
target: 'config',
|
||||
name: 'model',
|
||||
required: false,
|
||||
secret: false,
|
||||
hint: 'Bez vyplnění gpt-4o-mini. Platí pro všechny operace této služby.',
|
||||
},
|
||||
{
|
||||
id: 'organization',
|
||||
label: 'ID organizace',
|
||||
target: 'header',
|
||||
name: 'OpenAI-Organization',
|
||||
required: false,
|
||||
secret: false,
|
||||
hint: 'Jen když má účet víc organizací a útrata se má počítat konkrétní z nich.',
|
||||
},
|
||||
],
|
||||
// Seznam modelu: nejlevnejsi cteci volani, ktere vyzaduje platny klic.
|
||||
verifyPath: '/models',
|
||||
triggers: [],
|
||||
actions: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user