mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e24f31cbce | ||
|
|
3b9cbeaa49 | ||
|
|
c592e54118 | ||
|
|
d02499a915 | ||
|
|
c0bb85479d | ||
|
|
b595273c3f | ||
|
|
193a15aca4 | ||
|
|
39d5d797ec | ||
|
|
3fbd57caf1 | ||
|
|
3db8f82449 | ||
|
|
207a14970b | ||
|
|
0997989f36 | ||
|
|
7fd912d8a9 | ||
|
|
c550e4591c | ||
|
|
a881dc1877 | ||
|
|
da36c453b5 | ||
|
|
635823664c | ||
|
|
14846ab05c | ||
|
|
6c99c841f4 | ||
|
|
37d7902fcd | ||
|
|
1e563b1e0a | ||
|
|
b5da61377c | ||
|
|
1d62ece915 | ||
|
|
bbab2ff732 |
@@ -4151,7 +4151,7 @@ export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
|
||||
<circle r='108' fill='#d53' />
|
||||
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
|
||||
<circle r='96' fill='none' stroke='#ffffff' strokeWidth={7} />
|
||||
<path
|
||||
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
|
||||
fill='#ddd'
|
||||
|
||||
@@ -46,11 +46,11 @@ Durchsuchen Sie das Web mit Exa AI. Liefert relevante Suchergebnisse mit Titeln,
|
||||
| `type` | string | Nein | Suchtyp: neural, keyword, auto oder fast \(Standard: auto\) |
|
||||
| `includeDomains` | string | Nein | Kommagetrennte Liste von Domains, die in den Ergebnissen enthalten sein sollen |
|
||||
| `excludeDomains` | string | Nein | Kommagetrennte Liste von Domains, die aus den Ergebnissen ausgeschlossen werden sollen |
|
||||
| `category` | string | Nein | Nach Kategorie filtern: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `category` | string | Nein | Nach Kategorie filtern: company, research paper, news, pdf, github, tweet, personal site, linkedin profile, financial report |
|
||||
| `text` | boolean | Nein | Vollständigen Textinhalt in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `highlights` | boolean | Nein | Hervorgehobene Ausschnitte in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `summary` | boolean | Nein | KI-generierte Zusammenfassungen in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: always, fallback oder never \(Standard: never\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: never \(Standard\), fallback, always oder preferred \(immer livecrawl versuchen, bei Fehlschlag auf Cache zurückgreifen\) |
|
||||
| `apiKey` | string | Ja | Exa AI API-Schlüssel |
|
||||
|
||||
#### Ausgabe
|
||||
@@ -69,11 +69,11 @@ Ruft den Inhalt von Webseiten mit Exa AI ab. Gibt den Titel, Textinhalt und opti
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `urls` | string | Ja | Kommagetrennte Liste von URLs, von denen Inhalte abgerufen werden sollen |
|
||||
| `text` | boolean | Nein | Wenn true, gibt den vollständigen Seitentext mit Standardeinstellungen zurück. Wenn false, deaktiviert die Textrückgabe. |
|
||||
| `summaryQuery` | string | Nein | Abfrage zur Steuerung der Zusammenfassungserstellung |
|
||||
| `subpages` | number | Nein | Anzahl der Unterseiten, die von den angegebenen URLs gecrawlt werden sollen |
|
||||
| `summaryQuery` | string | Nein | Anfrage zur Steuerung der Zusammenfassungserstellung |
|
||||
| `subpages` | number | Nein | Anzahl der Unterseiten, die von den bereitgestellten URLs gecrawlt werden sollen |
|
||||
| `subpageTarget` | string | Nein | Kommagetrennte Schlüsselwörter zur Zielausrichtung auf bestimmte Unterseiten \(z.B. "docs,tutorial,about"\) |
|
||||
| `highlights` | boolean | Nein | Hervorgehobene Ausschnitte in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: always, fallback oder never \(Standard: never\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: never \(Standard\), fallback, always oder preferred \(immer livecrawl versuchen, bei Fehlschlag auf Cache zurückgreifen\) |
|
||||
| `apiKey` | string | Ja | Exa AI API-Schlüssel |
|
||||
|
||||
#### Ausgabe
|
||||
@@ -95,11 +95,10 @@ Finde Webseiten, die einer bestimmten URL ähnlich sind, mit Exa AI. Gibt eine L
|
||||
| `text` | boolean | Nein | Ob der vollständige Text der ähnlichen Seiten eingeschlossen werden soll |
|
||||
| `includeDomains` | string | Nein | Kommagetrennte Liste von Domains, die in den Ergebnissen enthalten sein sollen |
|
||||
| `excludeDomains` | string | Nein | Kommagetrennte Liste von Domains, die aus den Ergebnissen ausgeschlossen werden sollen |
|
||||
| `excludeSourceDomain` | boolean | Nein | Quell-Domain aus Ergebnissen ausschließen \(Standard: false\) |
|
||||
| `category` | string | Nein | Nach Kategorie filtern: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `excludeSourceDomain` | boolean | Nein | Die Quell-Domain aus den Ergebnissen ausschließen \(Standard: false\) |
|
||||
| `highlights` | boolean | Nein | Hervorgehobene Ausschnitte in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `summary` | boolean | Nein | KI-generierte Zusammenfassungen in Ergebnissen einschließen \(Standard: false\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: always, fallback oder never \(Standard: never\) |
|
||||
| `livecrawl` | string | Nein | Live-Crawling-Modus: never \(Standard\), fallback, always oder preferred \(versucht immer livecrawl, fällt auf Cache zurück, wenn es fehlschlägt\) |
|
||||
| `apiKey` | string | Ja | Exa AI API-Schlüssel |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -91,11 +91,11 @@ Führen Sie umfassende tiefgehende Recherchen im Web mit Parallel AI durch. Synt
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | Aufgabenstatus (laufend, abgeschlossen, fehlgeschlagen) |
|
||||
| `status` | string | Aufgabenstatus (abgeschlossen, fehlgeschlagen) |
|
||||
| `run_id` | string | Eindeutige ID für diese Rechercheaufgabe |
|
||||
| `message` | string | Statusmeldung (für laufende Aufgaben) |
|
||||
| `message` | string | Statusmeldung |
|
||||
| `content` | object | Rechercheergebnisse (strukturiert basierend auf output_schema) |
|
||||
| `basis` | array | Zitate und Quellen mit Auszügen und Vertrauensstufen |
|
||||
| `basis` | array | Zitate und Quellen mit Begründung und Vertrauensstufen |
|
||||
|
||||
## Hinweise
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ Generieren Sie Vervollständigungen mit Perplexity AI-Chatmodellen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Ergebnisse der Chat-Vervollständigung |
|
||||
| `content` | string | Generierter Textinhalt |
|
||||
| `model` | string | Für die Generierung verwendetes Modell |
|
||||
| `usage` | object | Informationen zur Token-Nutzung |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -76,8 +77,7 @@ Erhalte bewertete Suchergebnisse von Perplexity
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Suchergebnisse |
|
||||
| `results` | array | Array von Suchergebnissen |
|
||||
|
||||
## Hinweise
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Verarbeitet einen bereitgestellten Gedanken/eine Anweisung und macht ihn für na
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | string | Ja | Der vom Benutzer im Thinking Step-Block bereitgestellte Denkprozess oder die Anweisung. |
|
||||
| `thought` | string | Ja | Ihre interne Argumentation, Analyse oder Denkprozess. Nutzen Sie dies, um das Problem Schritt für Schritt zu durchdenken, bevor Sie antworten. |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -31,39 +31,32 @@ Integrieren Sie Übersetzen in den Workflow. Kann Text in jede Sprache übersetz
|
||||
|
||||
## Tools
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
Senden Sie eine Chat-Completion-Anfrage an jeden unterstützten LLM-Anbieter
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Ja | Das zu verwendende Modell (z.B. gpt-4o, claude-sonnet-4-5, gemini-2.0-flash) |
|
||||
| `systemPrompt` | string | Nein | System-Prompt zur Festlegung des Assistentenverhaltens |
|
||||
| `context` | string | Ja | Die Benutzernachricht oder der Kontext, der an das Modell gesendet wird |
|
||||
| `apiKey` | string | Nein | API-Schlüssel für den Anbieter (verwendet den Plattformschlüssel, wenn für gehostete Modelle nicht angegeben) |
|
||||
| `temperature` | number | Nein | Temperatur für die Antwortgenerierung (0-2) |
|
||||
| `maxTokens` | number | Nein | Maximale Tokens in der Antwort |
|
||||
| `azureEndpoint` | string | Nein | Azure OpenAI-Endpunkt-URL |
|
||||
| `azureApiVersion` | string | Nein | Azure OpenAI API-Version |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Übersetzter Text |
|
||||
| `model` | string | Verwendetes Modell |
|
||||
| `tokens` | json | Token-Nutzung |
|
||||
| `content` | string | Der generierte Antwortinhalt |
|
||||
| `model` | string | Das für die Generierung verwendete Modell |
|
||||
| `tokens` | object | Informationen zur Token-Nutzung |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Übersetzter Text |
|
||||
| `model` | string | Verwendetes Modell |
|
||||
| `tokens` | json | Token-Nutzung |
|
||||
|
||||
## Notizen
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
- Typ: `translate`
|
||||
|
||||
@@ -250,9 +250,9 @@ Eine Mediendatei (Bild, Video, Dokument) zu WordPress.com hochladen
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | WordPress.com-Website-ID oder Domain \(z.B. 12345678 oder meinwebsite.wordpress.com\) |
|
||||
| `file` | string | Ja | Base64-kodierte Dateidaten oder URL, von der die Datei abgerufen werden soll |
|
||||
| `filename` | string | Ja | Dateiname mit Erweiterung \(z.B. bild.jpg\) |
|
||||
| `siteId` | string | Ja | WordPress.com-Site-ID oder Domain \(z.B. 12345678 oder mysite.wordpress.com\) |
|
||||
| `file` | file | Nein | Hochzuladende Datei \(UserFile-Objekt\) |
|
||||
| `filename` | string | Nein | Optionale Überschreibung des Dateinamens \(z.B. bild.jpg\) |
|
||||
| `title` | string | Nein | Medientitel |
|
||||
| `caption` | string | Nein | Medienunterschrift |
|
||||
| `altText` | string | Nein | Alternativer Text für Barrierefreiheit |
|
||||
|
||||
@@ -170,28 +170,9 @@ Videos aus einer YouTube-Playlist abrufen.
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array von Videos in der Playlist |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
Finde Videos, die mit einem bestimmten YouTube-Video verwandt sind.
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | string | Ja | YouTube-Video-ID, für die verwandte Videos gefunden werden sollen |
|
||||
| `maxResults` | number | Nein | Maximale Anzahl der zurückzugebenden verwandten Videos \(1-50\) |
|
||||
| `pageToken` | string | Nein | Page-Token für Paginierung |
|
||||
| `apiKey` | string | Ja | YouTube API-Schlüssel |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array von verwandten Videos |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
Rufe Kommentare von einem YouTube-Video ab.
|
||||
Kommentare von einem YouTube-Video abrufen.
|
||||
|
||||
#### Eingabe
|
||||
|
||||
@@ -200,7 +181,7 @@ Rufe Kommentare von einem YouTube-Video ab.
|
||||
| `videoId` | string | Ja | YouTube-Video-ID |
|
||||
| `maxResults` | number | Nein | Maximale Anzahl der zurückzugebenden Kommentare |
|
||||
| `order` | string | Nein | Reihenfolge der Kommentare: time oder relevance |
|
||||
| `pageToken` | string | Nein | Page-Token für Paginierung |
|
||||
| `pageToken` | string | Nein | Seitentoken für Paginierung |
|
||||
| `apiKey` | string | Ja | YouTube API-Schlüssel |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -73,8 +73,9 @@ Eine Liste von Tickets aus Zendesk mit optionaler Filterung abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Ticket-Daten und Metadaten |
|
||||
| `tickets` | array | Array von Ticket-Objekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -93,8 +94,8 @@ Ein einzelnes Ticket anhand der ID von Zendesk abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Ticket-Daten |
|
||||
| `ticket` | object | Ticket-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -120,10 +121,10 @@ Ein neues Ticket in Zendesk erstellen mit Unterstützung für benutzerdefinierte
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Erstellte Ticket-Daten |
|
||||
| `ticket` | object | Erstelltes Ticket-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -140,10 +141,10 @@ Erstellen Sie mehrere Tickets in Zendesk auf einmal (maximal 100)
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Status des Massenerstell-Jobs |
|
||||
| `jobStatus` | object | Job-Status-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -171,8 +172,8 @@ Aktualisieren Sie ein bestehendes Ticket in Zendesk mit Unterstützung für benu
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Aktualisierte Ticket-Daten |
|
||||
| `ticket` | object | Aktualisiertes Ticket-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -196,8 +197,8 @@ Mehrere Tickets in Zendesk auf einmal aktualisieren (max. 100)
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Status des Massenaktualisierungsauftrags |
|
||||
| `jobStatus` | object | Job-Status-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -216,8 +217,8 @@ Ein Ticket aus Zendesk löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Löschbestätigung |
|
||||
| `deleted` | boolean | Löschvorgang erfolgreich |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -238,8 +239,8 @@ Mehrere Tickets in ein Zielticket zusammenführen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Status des Zusammenführungsauftrags |
|
||||
| `jobStatus` | object | Job-Status-Objekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -261,8 +262,9 @@ Eine Liste von Benutzern aus Zendesk mit optionaler Filterung abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Benutzerdaten und Metadaten |
|
||||
| `users` | array | Array von Benutzerobjekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -281,8 +283,8 @@ Einen einzelnen Benutzer anhand der ID von Zendesk abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Benutzerdaten |
|
||||
| `user` | object | Benutzerobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -300,8 +302,8 @@ Den aktuell authentifizierten Benutzer von Zendesk abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Daten des aktuellen Benutzers |
|
||||
| `user` | object | Aktuelles Benutzerobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -323,8 +325,9 @@ Nach Benutzern in Zendesk mit einer Abfragezeichenfolge suchen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Suchergebnisse für Benutzer |
|
||||
| `users` | array | Array von Benutzerobjekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -348,10 +351,10 @@ Einen neuen Benutzer in Zendesk erstellen
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Beschreibung |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Erstellte Benutzerdaten |
|
||||
| `user` | object | Erstelltes Benutzerobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -368,10 +371,10 @@ Erstellen mehrerer Benutzer in Zendesk mittels Massenimport
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Beschreibung |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Status des Massenimport-Jobs |
|
||||
| `jobStatus` | object | Job-Statusobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -398,8 +401,8 @@ Aktualisieren eines vorhandenen Benutzers in Zendesk
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Aktualisierte Benutzerdaten |
|
||||
| `user` | object | Aktualisiertes Benutzerobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -418,8 +421,8 @@ Mehrere Benutzer in Zendesk über Massenaktualisierung aktualisieren
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Status des Massenaktualisierungsauftrags |
|
||||
| `jobStatus` | object | Job-Statusobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -438,8 +441,8 @@ Einen Benutzer aus Zendesk löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Daten des gelöschten Benutzers |
|
||||
| `deleted` | boolean | Löscherfolg |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -459,8 +462,9 @@ Eine Liste von Organisationen von Zendesk abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Organisationsdaten und Metadaten |
|
||||
| `organizations` | array | Array von Organisationsobjekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -479,8 +483,8 @@ Eine einzelne Organisation anhand der ID von Zendesk abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Organisationsdaten |
|
||||
| `organization` | object | Organisationsobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -499,10 +503,11 @@ Autovervollständigung von Organisationen in Zendesk nach Namenspräfix (für Na
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Beschreibung |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Suchergebnisse für Organisationen |
|
||||
| `organizations` | array | Array von Organisationsobjekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -524,10 +529,10 @@ Eine neue Organisation in Zendesk erstellen
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Beschreibung |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Daten der erstellten Organisation |
|
||||
| `organization` | object | Erstelltes Organisationsobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -546,8 +551,8 @@ Mehrere Organisationen in Zendesk über Massenimport erstellen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Status des Massenerfassungsauftrags |
|
||||
| `jobStatus` | object | Job-Statusobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -572,8 +577,8 @@ Eine bestehende Organisation in Zendesk aktualisieren
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Aktualisierte Organisationsdaten |
|
||||
| `organization` | object | Aktualisiertes Organisationsobjekt |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -592,8 +597,8 @@ Eine Organisation aus Zendesk löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Gelöschte Organisationsdaten |
|
||||
| `deleted` | boolean | Löscherfolg |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -616,8 +621,9 @@ Einheitliche Suche über Tickets, Benutzer und Organisationen in Zendesk
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Suchergebnisse |
|
||||
| `results` | array | Array von Ergebnisobjekten |
|
||||
| `paging` | object | Paginierungsinformationen |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -636,8 +642,8 @@ Zählen der Anzahl von Suchergebnissen, die einer Abfrage in Zendesk entsprechen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Suchergebnis-Anzahl |
|
||||
| `count` | number | Anzahl der übereinstimmenden Ergebnisse |
|
||||
| `metadata` | object | Operationsmetadaten |
|
||||
|
||||
## Hinweise
|
||||
|
||||
|
||||
@@ -49,11 +49,11 @@ Search the web using Exa AI. Returns relevant search results with titles, URLs,
|
||||
| `type` | string | No | Search type: neural, keyword, auto or fast \(default: auto\) |
|
||||
| `includeDomains` | string | No | Comma-separated list of domains to include in results |
|
||||
| `excludeDomains` | string | No | Comma-separated list of domains to exclude from results |
|
||||
| `category` | string | No | Filter by category: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `category` | string | No | Filter by category: company, research paper, news, pdf, github, tweet, personal site, linkedin profile, financial report |
|
||||
| `text` | boolean | No | Include full text content in results \(default: false\) |
|
||||
| `highlights` | boolean | No | Include highlighted snippets in results \(default: false\) |
|
||||
| `summary` | boolean | No | Include AI-generated summaries in results \(default: false\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: always, fallback, or never \(default: never\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: never \(default\), fallback, always, or preferred \(always try livecrawl, fall back to cache if fails\) |
|
||||
| `apiKey` | string | Yes | Exa AI API Key |
|
||||
|
||||
#### Output
|
||||
@@ -76,7 +76,7 @@ Retrieve the contents of webpages using Exa AI. Returns the title, text content,
|
||||
| `subpages` | number | No | Number of subpages to crawl from the provided URLs |
|
||||
| `subpageTarget` | string | No | Comma-separated keywords to target specific subpages \(e.g., "docs,tutorial,about"\) |
|
||||
| `highlights` | boolean | No | Include highlighted snippets in results \(default: false\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: always, fallback, or never \(default: never\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: never \(default\), fallback, always, or preferred \(always try livecrawl, fall back to cache if fails\) |
|
||||
| `apiKey` | string | Yes | Exa AI API Key |
|
||||
|
||||
#### Output
|
||||
@@ -99,10 +99,9 @@ Find webpages similar to a given URL using Exa AI. Returns a list of similar lin
|
||||
| `includeDomains` | string | No | Comma-separated list of domains to include in results |
|
||||
| `excludeDomains` | string | No | Comma-separated list of domains to exclude from results |
|
||||
| `excludeSourceDomain` | boolean | No | Exclude the source domain from results \(default: false\) |
|
||||
| `category` | string | No | Filter by category: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `highlights` | boolean | No | Include highlighted snippets in results \(default: false\) |
|
||||
| `summary` | boolean | No | Include AI-generated summaries in results \(default: false\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: always, fallback, or never \(default: never\) |
|
||||
| `livecrawl` | string | No | Live crawling mode: never \(default\), fallback, always, or preferred \(always try livecrawl, fall back to cache if fails\) |
|
||||
| `apiKey` | string | Yes | Exa AI API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -94,11 +94,11 @@ Conduct comprehensive deep research across the web using Parallel AI. Synthesize
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | Task status \(running, completed, failed\) |
|
||||
| `status` | string | Task status \(completed, failed\) |
|
||||
| `run_id` | string | Unique ID for this research task |
|
||||
| `message` | string | Status message \(for running tasks\) |
|
||||
| `message` | string | Status message |
|
||||
| `content` | object | Research results \(structured based on output_schema\) |
|
||||
| `basis` | array | Citations and sources with excerpts and confidence levels |
|
||||
| `basis` | array | Citations and sources with reasoning and confidence levels |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -54,8 +54,9 @@ Generate completions using Perplexity AI chat models
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Chat completion results |
|
||||
| `content` | string | Generated text content |
|
||||
| `model` | string | Model used for generation |
|
||||
| `usage` | object | Token usage information |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -79,8 +80,7 @@ Get ranked search results from Perplexity
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search results |
|
||||
| `results` | array | Array of search results |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Processes a provided thought/instruction, making it available for subsequent ste
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | string | Yes | The thought process or instruction provided by the user in the Thinking Step block. |
|
||||
| `thought` | string | Yes | Your internal reasoning, analysis, or thought process. Use this to think through the problem step by step before responding. |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -34,38 +34,30 @@ Integrate Translate into the workflow. Can translate text to any language.
|
||||
|
||||
## Tools
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
Send a chat completion request to any supported LLM provider
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Yes | The model to use \(e.g., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | No | System prompt to set the behavior of the assistant |
|
||||
| `context` | string | Yes | The user message or context to send to the model |
|
||||
| `apiKey` | string | No | API key for the provider \(uses platform key if not provided for hosted models\) |
|
||||
| `temperature` | number | No | Temperature for response generation \(0-2\) |
|
||||
| `maxTokens` | number | No | Maximum tokens in the response |
|
||||
| `azureEndpoint` | string | No | Azure OpenAI endpoint URL |
|
||||
| `azureApiVersion` | string | No | Azure OpenAI API version |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
| `content` | string | The generated response content |
|
||||
| `model` | string | The model used for generation |
|
||||
| `tokens` | object | Token usage information |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -254,8 +254,8 @@ Upload a media file (image, video, document) to WordPress.com
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `file` | string | Yes | Base64 encoded file data or URL to fetch file from |
|
||||
| `filename` | string | Yes | Filename with extension \(e.g., image.jpg\) |
|
||||
| `file` | file | No | File to upload \(UserFile object\) |
|
||||
| `filename` | string | No | Optional filename override \(e.g., image.jpg\) |
|
||||
| `title` | string | No | Media title |
|
||||
| `caption` | string | No | Media caption |
|
||||
| `altText` | string | No | Alternative text for accessibility |
|
||||
|
||||
@@ -173,25 +173,6 @@ Get videos from a YouTube playlist.
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of videos in the playlist |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
Find videos related to a specific YouTube video.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | string | Yes | YouTube video ID to find related videos for |
|
||||
| `maxResults` | number | No | Maximum number of related videos to return \(1-50\) |
|
||||
| `pageToken` | string | No | Page token for pagination |
|
||||
| `apiKey` | string | Yes | YouTube API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of related videos |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
Get comments from a YouTube video.
|
||||
|
||||
@@ -76,8 +76,9 @@ Retrieve a list of tickets from Zendesk with optional filtering
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Tickets data and metadata |
|
||||
| `tickets` | array | Array of ticket objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -96,8 +97,8 @@ Get a single ticket by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Ticket data |
|
||||
| `ticket` | object | Ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -125,8 +126,8 @@ Create a new ticket in Zendesk with support for custom fields
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created ticket data |
|
||||
| `ticket` | object | Created ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -145,8 +146,8 @@ Create multiple tickets in Zendesk at once (max 100)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk create job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -174,8 +175,8 @@ Update an existing ticket in Zendesk with support for custom fields
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated ticket data |
|
||||
| `ticket` | object | Updated ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -199,8 +200,8 @@ Update multiple tickets in Zendesk at once (max 100)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk update job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -219,8 +220,8 @@ Delete a ticket from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Delete confirmation |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -241,8 +242,8 @@ Merge multiple tickets into a target ticket
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Merge job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -264,8 +265,9 @@ Retrieve a list of users from Zendesk with optional filtering
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Users data and metadata |
|
||||
| `users` | array | Array of user objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -284,8 +286,8 @@ Get a single user by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | User data |
|
||||
| `user` | object | User object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -303,8 +305,8 @@ Get the currently authenticated user from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Current user data |
|
||||
| `user` | object | Current user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -326,8 +328,9 @@ Search for users in Zendesk using a query string
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Users search results |
|
||||
| `users` | array | Array of user objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -353,8 +356,8 @@ Create a new user in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created user data |
|
||||
| `user` | object | Created user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -373,8 +376,8 @@ Create multiple users in Zendesk using bulk import
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk creation job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -401,8 +404,8 @@ Update an existing user in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated user data |
|
||||
| `user` | object | Updated user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -421,8 +424,8 @@ Update multiple users in Zendesk using bulk update
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk update job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -441,8 +444,8 @@ Delete a user from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deleted user data |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -462,8 +465,9 @@ Retrieve a list of organizations from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organizations data and metadata |
|
||||
| `organizations` | array | Array of organization objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -482,8 +486,8 @@ Get a single organization by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organization data |
|
||||
| `organization` | object | Organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -504,8 +508,9 @@ Autocomplete organizations in Zendesk by name prefix (for name matching/autocomp
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organizations search results |
|
||||
| `organizations` | array | Array of organization objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -529,8 +534,8 @@ Create a new organization in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created organization data |
|
||||
| `organization` | object | Created organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -549,8 +554,8 @@ Create multiple organizations in Zendesk using bulk import
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk creation job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -575,8 +580,8 @@ Update an existing organization in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated organization data |
|
||||
| `organization` | object | Updated organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -595,8 +600,8 @@ Delete an organization from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deleted organization data |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -619,8 +624,9 @@ Unified search across tickets, users, and organizations in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search results |
|
||||
| `results` | array | Array of result objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -639,8 +645,8 @@ Count the number of search results matching a query in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search count result |
|
||||
| `count` | number | Number of matching results |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ Busca en la web usando Exa AI. Devuelve resultados de búsqueda relevantes con t
|
||||
| `type` | string | No | Tipo de búsqueda: neural, keyword, auto o fast \(predeterminado: auto\) |
|
||||
| `includeDomains` | string | No | Lista separada por comas de dominios a incluir en los resultados |
|
||||
| `excludeDomains` | string | No | Lista separada por comas de dominios a excluir de los resultados |
|
||||
| `category` | string | No | Filtrar por categoría: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `category` | string | No | Filtrar por categoría: company, research paper, news, pdf, github, tweet, personal site, linkedin profile, financial report |
|
||||
| `text` | boolean | No | Incluir contenido de texto completo en los resultados \(predeterminado: false\) |
|
||||
| `highlights` | boolean | No | Incluir fragmentos destacados en los resultados \(predeterminado: false\) |
|
||||
| `summary` | boolean | No | Incluir resúmenes generados por IA en los resultados \(predeterminado: false\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: always, fallback o never \(predeterminado: never\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: never \(predeterminado\), fallback, always, o preferred \(siempre intenta livecrawl, recurre a caché si falla\) |
|
||||
| `apiKey` | string | Sí | Clave API de Exa AI |
|
||||
|
||||
#### Salida
|
||||
@@ -67,13 +67,13 @@ Recupera el contenido de páginas web usando Exa AI. Devuelve el título, conten
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `urls` | string | Sí | Lista separada por comas de URLs de las que recuperar contenido |
|
||||
| `urls` | string | Sí | Lista separada por comas de URLs para recuperar contenido |
|
||||
| `text` | boolean | No | Si es true, devuelve el texto completo de la página con la configuración predeterminada. Si es false, desactiva la devolución de texto. |
|
||||
| `summaryQuery` | string | No | Consulta para guiar la generación del resumen |
|
||||
| `subpages` | number | No | Número de subpáginas a rastrear desde las URLs proporcionadas |
|
||||
| `subpageTarget` | string | No | Palabras clave separadas por comas para dirigirse a subpáginas específicas \(por ejemplo, "docs,tutorial,about"\) |
|
||||
| `highlights` | boolean | No | Incluir fragmentos destacados en los resultados \(predeterminado: false\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: always, fallback o never \(predeterminado: never\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: never \(predeterminado\), fallback, always, o preferred \(siempre intenta livecrawl, recurre a caché si falla\) |
|
||||
| `apiKey` | string | Sí | Clave API de Exa AI |
|
||||
|
||||
#### Salida
|
||||
@@ -96,10 +96,9 @@ Encuentra páginas web similares a una URL determinada utilizando Exa AI. Devuel
|
||||
| `includeDomains` | string | No | Lista separada por comas de dominios a incluir en los resultados |
|
||||
| `excludeDomains` | string | No | Lista separada por comas de dominios a excluir de los resultados |
|
||||
| `excludeSourceDomain` | boolean | No | Excluir el dominio de origen de los resultados \(predeterminado: false\) |
|
||||
| `category` | string | No | Filtrar por categoría: company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `highlights` | boolean | No | Incluir fragmentos destacados en los resultados \(predeterminado: false\) |
|
||||
| `summary` | boolean | No | Incluir resúmenes generados por IA en los resultados \(predeterminado: false\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: always, fallback o never \(predeterminado: never\) |
|
||||
| `livecrawl` | string | No | Modo de rastreo en vivo: never \(predeterminado\), fallback, always o preferred \(siempre intenta rastreo en vivo, recurre a caché si falla\) |
|
||||
| `apiKey` | string | Sí | Clave API de Exa AI |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -91,11 +91,11 @@ Realiza investigaciones exhaustivas y profundas en la web utilizando Parallel AI
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | Estado de la tarea (en ejecución, completada, fallida) |
|
||||
| `status` | string | Estado de la tarea (completada, fallida) |
|
||||
| `run_id` | string | ID único para esta tarea de investigación |
|
||||
| `message` | string | Mensaje de estado (para tareas en ejecución) |
|
||||
| `message` | string | Mensaje de estado |
|
||||
| `content` | object | Resultados de la investigación (estructurados según output_schema) |
|
||||
| `basis` | array | Citas y fuentes con extractos y niveles de confianza |
|
||||
| `basis` | array | Citas y fuentes con razonamiento y niveles de confianza |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ Genera completados utilizando los modelos de chat de Perplexity AI
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados del completado de chat |
|
||||
| `content` | string | Contenido de texto generado |
|
||||
| `model` | string | Modelo utilizado para la generación |
|
||||
| `usage` | object | Información de uso de tokens |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -76,8 +77,7 @@ Obtén resultados de búsqueda clasificados de Perplexity
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados de búsqueda |
|
||||
| `results` | array | Array de resultados de búsqueda |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ Procesa un pensamiento/instrucción proporcionado, haciéndolo disponible para l
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | string | Sí | El proceso de pensamiento o instrucción proporcionado por el usuario en el bloque de Paso de Pensamiento. |
|
||||
| `thought` | string | Sí | Tu razonamiento interno, análisis o proceso de pensamiento. Utiliza esto para analizar el problema paso a paso antes de responder. |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -31,37 +31,30 @@ Integra Translate en el flujo de trabajo. Puede traducir texto a cualquier idiom
|
||||
|
||||
## Herramientas
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
Envía una solicitud de completado de chat a cualquier proveedor de LLM compatible
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Sí | El modelo a utilizar \(p. ej., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | No | Prompt del sistema para establecer el comportamiento del asistente |
|
||||
| `context` | string | Sí | El mensaje del usuario o contexto para enviar al modelo |
|
||||
| `apiKey` | string | No | Clave API para el proveedor \(usa la clave de la plataforma si no se proporciona para modelos alojados\) |
|
||||
| `temperature` | number | No | Temperatura para la generación de respuestas \(0-2\) |
|
||||
| `maxTokens` | number | No | Tokens máximos en la respuesta |
|
||||
| `azureEndpoint` | string | No | URL del endpoint de Azure OpenAI |
|
||||
| `azureApiVersion` | string | No | Versión de la API de Azure OpenAI |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Texto traducido |
|
||||
| `model` | string | Modelo utilizado |
|
||||
| `tokens` | json | Uso de tokens |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Texto traducido |
|
||||
| `model` | string | Modelo utilizado |
|
||||
| `tokens` | json | Uso de tokens |
|
||||
| `content` | string | El contenido de la respuesta generada |
|
||||
| `model` | string | El modelo utilizado para la generación |
|
||||
| `tokens` | object | Información de uso de tokens |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
@@ -251,12 +251,12 @@ Subir un archivo multimedia (imagen, video, documento) a WordPress.com
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio o dominio de WordPress.com \(p. ej., 12345678 o misitio.wordpress.com\) |
|
||||
| `file` | string | Sí | Datos del archivo codificados en Base64 o URL para obtener el archivo |
|
||||
| `filename` | string | Sí | Nombre del archivo con extensión \(p. ej., imagen.jpg\) |
|
||||
| `title` | string | No | Título del archivo multimedia |
|
||||
| `caption` | string | No | Leyenda del archivo multimedia |
|
||||
| `file` | file | No | Archivo para subir \(objeto UserFile\) |
|
||||
| `filename` | string | No | Anulación opcional del nombre de archivo \(p. ej., imagen.jpg\) |
|
||||
| `title` | string | No | Título del medio |
|
||||
| `caption` | string | No | Leyenda del medio |
|
||||
| `altText` | string | No | Texto alternativo para accesibilidad |
|
||||
| `description` | string | No | Descripción del archivo multimedia |
|
||||
| `description` | string | No | Descripción del medio |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -170,28 +170,9 @@ Obtener videos de una lista de reproducción de YouTube.
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array de videos en la lista de reproducción |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
Encuentra videos relacionados con un video específico de YouTube.
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `videoId` | string | Sí | ID del video de YouTube para encontrar videos relacionados |
|
||||
| `maxResults` | number | No | Número máximo de videos relacionados a devolver \(1-50\) |
|
||||
| `pageToken` | string | No | Token de página para paginación |
|
||||
| `apiKey` | string | Sí | Clave API de YouTube |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array de videos relacionados |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
Obtiene comentarios de un video de YouTube.
|
||||
Obtener comentarios de un video de YouTube.
|
||||
|
||||
#### Entrada
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ Recupera una lista de tickets de Zendesk con filtrado opcional
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos y metadatos de los tickets |
|
||||
| `tickets` | array | Array de objetos de ticket |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -93,8 +94,8 @@ Obtener un solo ticket por ID desde Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del ticket |
|
||||
| `ticket` | object | Objeto de ticket |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -122,8 +123,8 @@ Crear un nuevo ticket en Zendesk con soporte para campos personalizados
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del ticket creado |
|
||||
| `ticket` | object | Objeto de ticket creado |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -142,8 +143,8 @@ Crear múltiples tickets en Zendesk a la vez (máximo 100)
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de creación masiva |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -171,8 +172,8 @@ Actualizar un ticket existente en Zendesk con soporte para campos personalizados
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos actualizados del ticket |
|
||||
| `ticket` | object | Objeto de ticket actualizado |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -196,8 +197,8 @@ Actualizar múltiples tickets en Zendesk a la vez (máximo 100)
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de actualización masiva |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -216,8 +217,8 @@ Eliminar un ticket de Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Confirmación de eliminación |
|
||||
| `deleted` | boolean | Éxito de la eliminación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -238,8 +239,8 @@ Fusionar múltiples tickets en un ticket objetivo
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de fusión |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -261,8 +262,9 @@ Recuperar una lista de usuarios de Zendesk con filtrado opcional
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de usuarios y metadatos |
|
||||
| `users` | array | Array de objetos de usuario |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -281,8 +283,8 @@ Obtener un solo usuario por ID desde Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del usuario |
|
||||
| `user` | object | Objeto de usuario |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -300,8 +302,8 @@ Obtener el usuario actualmente autenticado desde Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del usuario actual |
|
||||
| `user` | object | Objeto del usuario actual |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -323,8 +325,9 @@ Buscar usuarios en Zendesk usando una cadena de consulta
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados de búsqueda de usuarios |
|
||||
| `users` | array | Array de objetos de usuario |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -350,8 +353,8 @@ Crear un nuevo usuario en Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del usuario creado |
|
||||
| `user` | object | Objeto del usuario creado |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -370,8 +373,8 @@ Crear múltiples usuarios en Zendesk mediante importación masiva
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de creación masiva |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -398,8 +401,8 @@ Actualizar un usuario existente en Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos actualizados del usuario |
|
||||
| `user` | object | Objeto del usuario actualizado |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -418,8 +421,8 @@ Actualizar múltiples usuarios en Zendesk usando actualización masiva
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de actualización masiva |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -438,8 +441,8 @@ Eliminar un usuario de Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos del usuario eliminado |
|
||||
| `deleted` | boolean | Éxito de eliminación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -459,8 +462,9 @@ Obtener una lista de organizaciones de Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos y metadatos de las organizaciones |
|
||||
| `organizations` | array | Array de objetos de organización |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -479,8 +483,8 @@ Obtener una única organización por ID desde Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de la organización |
|
||||
| `organization` | object | Objeto de organización |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -501,8 +505,9 @@ Autocompletar organizaciones en Zendesk por prefijo de nombre (para coincidencia
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados de búsqueda de organizaciones |
|
||||
| `organizations` | array | Array de objetos de organización |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -526,8 +531,8 @@ Crear una nueva organización en Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de la organización creada |
|
||||
| `organization` | object | Objeto de organización creada |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -546,8 +551,8 @@ Crear múltiples organizaciones en Zendesk mediante importación masiva
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Estado del trabajo de creación masiva |
|
||||
| `jobStatus` | object | Objeto de estado del trabajo |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -572,8 +577,8 @@ Actualizar una organización existente en Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de la organización actualizada |
|
||||
| `organization` | object | Objeto de organización actualizada |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -592,8 +597,8 @@ Eliminar una organización de Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de la organización eliminada |
|
||||
| `deleted` | boolean | Éxito de eliminación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -616,8 +621,9 @@ Búsqueda unificada a través de tickets, usuarios y organizaciones en Zendesk
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados de la búsqueda |
|
||||
| `results` | array | Array de objetos de resultado |
|
||||
| `paging` | object | Información de paginación |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -636,8 +642,8 @@ Contar el número de resultados de búsqueda que coinciden con una consulta en Z
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultado del recuento de búsqueda |
|
||||
| `count` | number | Número de resultados coincidentes |
|
||||
| `metadata` | object | Metadatos de la operación |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
@@ -42,15 +42,15 @@ Recherchez sur le web en utilisant Exa AI. Renvoie des résultats de recherche p
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `query` | chaîne | Oui | La requête de recherche à exécuter |
|
||||
| `numResults` | nombre | Non | Nombre de résultats à retourner \(par défaut : 10, max : 25\) |
|
||||
| `useAutoprompt` | booléen | Non | Utiliser l'autoprompt pour améliorer la requête \(par défaut : false\) |
|
||||
| `useAutoprompt` | booléen | Non | Utiliser ou non l'autoprompt pour améliorer la requête \(par défaut : false\) |
|
||||
| `type` | chaîne | Non | Type de recherche : neural, keyword, auto ou fast \(par défaut : auto\) |
|
||||
| `includeDomains` | chaîne | Non | Liste de domaines à inclure dans les résultats, séparés par des virgules |
|
||||
| `excludeDomains` | chaîne | Non | Liste de domaines à exclure des résultats, séparés par des virgules |
|
||||
| `category` | chaîne | Non | Filtrer par catégorie : company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `category` | chaîne | Non | Filtrer par catégorie : company, research paper, news, pdf, github, tweet, personal site, linkedin profile, financial report |
|
||||
| `text` | booléen | Non | Inclure le contenu textuel complet dans les résultats \(par défaut : false\) |
|
||||
| `highlights` | booléen | Non | Inclure des extraits surlignés dans les résultats \(par défaut : false\) |
|
||||
| `summary` | booléen | Non | Inclure des résumés générés par IA dans les résultats \(par défaut : false\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : always, fallback, ou never \(par défaut : never\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : never \(par défaut\), fallback, always, ou preferred \(toujours essayer l'exploration en direct, revenir au cache en cas d'échec\) |
|
||||
| `apiKey` | chaîne | Oui | Clé API Exa AI |
|
||||
|
||||
#### Sortie
|
||||
@@ -67,13 +67,13 @@ Récupérer le contenu des pages web en utilisant Exa AI. Renvoie le titre, le c
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `urls` | chaîne | Oui | Liste d'URLs séparées par des virgules pour récupérer du contenu |
|
||||
| `text` | booléen | Non | Si vrai, renvoie le texte complet de la page avec les paramètres par défaut. Si faux, désactive le retour de texte. |
|
||||
| `urls` | chaîne | Oui | Liste d'URLs séparées par des virgules pour récupérer le contenu |
|
||||
| `text` | booléen | Non | Si true, renvoie le texte complet de la page avec les paramètres par défaut. Si false, désactive le retour du texte. |
|
||||
| `summaryQuery` | chaîne | Non | Requête pour guider la génération du résumé |
|
||||
| `subpages` | nombre | Non | Nombre de sous-pages à explorer à partir des URLs fournies |
|
||||
| `subpageTarget` | chaîne | Non | Mots-clés séparés par des virgules pour cibler des sous-pages spécifiques \(ex. : "docs,tutorial,about"\) |
|
||||
| `subpageTarget` | chaîne | Non | Mots-clés séparés par des virgules pour cibler des sous-pages spécifiques \(par exemple, "docs,tutorial,about"\) |
|
||||
| `highlights` | booléen | Non | Inclure des extraits surlignés dans les résultats \(par défaut : false\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : always, fallback, ou never \(par défaut : never\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : never \(par défaut\), fallback, always, ou preferred \(toujours essayer l'exploration en direct, revenir au cache en cas d'échec\) |
|
||||
| `apiKey` | chaîne | Oui | Clé API Exa AI |
|
||||
|
||||
#### Sortie
|
||||
@@ -96,10 +96,9 @@ Trouvez des pages web similaires à une URL donnée en utilisant Exa AI. Renvoie
|
||||
| `includeDomains` | chaîne | Non | Liste de domaines à inclure dans les résultats, séparés par des virgules |
|
||||
| `excludeDomains` | chaîne | Non | Liste de domaines à exclure des résultats, séparés par des virgules |
|
||||
| `excludeSourceDomain` | booléen | Non | Exclure le domaine source des résultats \(par défaut : false\) |
|
||||
| `category` | chaîne | Non | Filtrer par catégorie : company, research_paper, news_article, pdf, github, tweet, movie, song, personal_site |
|
||||
| `highlights` | booléen | Non | Inclure des extraits surlignés dans les résultats \(par défaut : false\) |
|
||||
| `summary` | booléen | Non | Inclure des résumés générés par IA dans les résultats \(par défaut : false\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : always, fallback, ou never \(par défaut : never\) |
|
||||
| `livecrawl` | chaîne | Non | Mode d'exploration en direct : never \(par défaut\), fallback, always, ou preferred \(toujours essayer l'exploration en direct, revenir au cache en cas d'échec\) |
|
||||
| `apiKey` | chaîne | Oui | Clé API Exa AI |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -91,11 +91,11 @@ Menez des recherches approfondies complètes sur le web en utilisant Parallel AI
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | Statut de la tâche (en cours, terminée, échouée) |
|
||||
| `status` | string | Statut de la tâche (terminée, échouée) |
|
||||
| `run_id` | string | ID unique pour cette tâche de recherche |
|
||||
| `message` | string | Message de statut (pour les tâches en cours) |
|
||||
| `message` | string | Message de statut |
|
||||
| `content` | object | Résultats de recherche (structurés selon output_schema) |
|
||||
| `basis` | array | Citations et sources avec extraits et niveaux de confiance |
|
||||
| `basis` | array | Citations et sources avec raisonnement et niveaux de confiance |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ Générez des compléments à l'aide des modèles de chat Perplexity AI
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Résultats du complément de chat |
|
||||
| `content` | string | Contenu de texte généré |
|
||||
| `model` | string | Modèle utilisé pour la génération |
|
||||
| `usage` | object | Informations sur l'utilisation des tokens |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -76,8 +77,7 @@ Obtenez des résultats de recherche classés depuis Perplexity
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Résultats de recherche |
|
||||
| `results` | array | Tableau des résultats de recherche |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ Traite une réflexion/instruction fournie, la rendant disponible pour les étape
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `thought` | chaîne | Oui | Le processus de réflexion ou l'instruction fournie par l'utilisateur dans le bloc Étape de Réflexion. |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | chaîne | Oui | Votre raisonnement interne, analyse ou processus de réflexion. Utilisez ceci pour réfléchir au problème étape par étape avant de répondre. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -31,37 +31,30 @@ Intégrez Translate dans le flux de travail. Peut traduire du texte dans n'impor
|
||||
|
||||
## Outils
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
Envoyez une requête de complétion de chat à n'importe quel fournisseur de LLM pris en charge
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `model` | chaîne | Oui | Le modèle à utiliser (ex. : gpt-4o, claude-sonnet-4-5, gemini-2.0-flash) |
|
||||
| `systemPrompt` | chaîne | Non | Instruction système pour définir le comportement de l'assistant |
|
||||
| `context` | chaîne | Oui | Le message utilisateur ou le contexte à envoyer au modèle |
|
||||
| `apiKey` | chaîne | Non | Clé API pour le fournisseur (utilise la clé de plateforme si non fournie pour les modèles hébergés) |
|
||||
| `temperature` | nombre | Non | Température pour la génération de réponse (0-2) |
|
||||
| `maxTokens` | nombre | Non | Nombre maximum de tokens dans la réponse |
|
||||
| `azureEndpoint` | chaîne | Non | URL du point de terminaison Azure OpenAI |
|
||||
| `azureApiVersion` | chaîne | Non | Version de l'API Azure OpenAI |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Texte traduit |
|
||||
| `model` | string | Modèle utilisé |
|
||||
| `tokens` | json | Utilisation des tokens |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Texte traduit |
|
||||
| `model` | string | Modèle utilisé |
|
||||
| `tokens` | json | Utilisation des jetons |
|
||||
| `content` | chaîne | Le contenu de la réponse générée |
|
||||
| `model` | chaîne | Le modèle utilisé pour la génération |
|
||||
| `tokens` | objet | Informations sur l'utilisation des tokens |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -250,13 +250,13 @@ Télécharger un fichier média (image, vidéo, document) sur WordPress.com
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site WordPress.com ou domaine \(ex., 12345678 ou monsite.wordpress.com\) |
|
||||
| `file` | string | Oui | Données du fichier encodées en Base64 ou URL pour récupérer le fichier |
|
||||
| `filename` | string | Oui | Nom du fichier avec extension \(ex., image.jpg\) |
|
||||
| `title` | string | Non | Titre du média |
|
||||
| `caption` | string | Non | Légende du média |
|
||||
| `altText` | string | Non | Texte alternatif pour l'accessibilité |
|
||||
| `description` | string | Non | Description du média |
|
||||
| `siteId` | chaîne | Oui | ID du site WordPress.com ou domaine \(ex., 12345678 ou monsite.wordpress.com\) |
|
||||
| `file` | fichier | Non | Fichier à télécharger \(objet UserFile\) |
|
||||
| `filename` | chaîne | Non | Remplacement optionnel du nom de fichier \(ex., image.jpg\) |
|
||||
| `title` | chaîne | Non | Titre du média |
|
||||
| `caption` | chaîne | Non | Légende du média |
|
||||
| `altText` | chaîne | Non | Texte alternatif pour l'accessibilité |
|
||||
| `description` | chaîne | Non | Description du média |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -170,25 +170,6 @@ Obtenir les vidéos d'une playlist YouTube.
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | tableau | Tableau de vidéos dans la playlist |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
Trouver des vidéos liées à une vidéo YouTube spécifique.
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `videoId` | chaîne | Oui | ID de la vidéo YouTube pour laquelle trouver des vidéos liées |
|
||||
| `maxResults` | nombre | Non | Nombre maximum de vidéos liées à retourner \(1-50\) |
|
||||
| `pageToken` | chaîne | Non | Jeton de page pour la pagination |
|
||||
| `apiKey` | chaîne | Oui | Clé API YouTube |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | tableau | Tableau de vidéos liées |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
Obtenir les commentaires d'une vidéo YouTube.
|
||||
@@ -207,7 +188,7 @@ Obtenir les commentaires d'une vidéo YouTube.
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | tableau | Tableau de commentaires de la vidéo |
|
||||
| `items` | tableau | Tableau des commentaires de la vidéo |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -74,8 +74,9 @@ Récupérer une liste de tickets depuis Zendesk avec filtrage optionnel
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données et métadonnées des tickets |
|
||||
| `tickets` | array | Tableau d'objets ticket |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -94,8 +95,8 @@ Obtenir un ticket unique par ID depuis Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données du ticket |
|
||||
| `ticket` | object | Objet ticket |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -123,8 +124,8 @@ Créer un nouveau ticket dans Zendesk avec prise en charge des champs personnali
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données du ticket créé |
|
||||
| `ticket` | object | Objet ticket créé |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -143,8 +144,8 @@ Créer plusieurs tickets dans Zendesk en une seule fois (max 100)
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de création en masse |
|
||||
| `jobStatus` | object | Objet statut de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -172,8 +173,8 @@ Mettre à jour un ticket existant dans Zendesk avec prise en charge des champs p
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données du ticket mis à jour |
|
||||
| `ticket` | object | Objet ticket mis à jour |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -197,8 +198,8 @@ Mettre à jour plusieurs tickets dans Zendesk en une seule fois (max 100)
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de mise à jour groupée |
|
||||
| `jobStatus` | object | Objet statut de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -217,8 +218,8 @@ Supprimer un ticket de Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Confirmation de suppression |
|
||||
| `deleted` | boolean | Succès de la suppression |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -239,8 +240,8 @@ Fusionner plusieurs tickets dans un ticket cible
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de fusion |
|
||||
| `jobStatus` | object | Objet statut de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -262,8 +263,9 @@ Récupérer une liste d'utilisateurs de Zendesk avec filtrage optionnel
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données utilisateurs et métadonnées |
|
||||
| `users` | array | Tableau d'objets utilisateur |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -282,8 +284,8 @@ Obtenir un utilisateur unique par ID depuis Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'utilisateur |
|
||||
| `user` | object | Objet utilisateur |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -301,8 +303,8 @@ Obtenir l'utilisateur actuellement authentifié depuis Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'utilisateur actuel |
|
||||
| `user` | object | Objet de l'utilisateur actuel |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -324,8 +326,9 @@ Rechercher des utilisateurs dans Zendesk à l'aide d'une chaîne de requête
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Résultats de recherche d'utilisateurs |
|
||||
| `users` | array | Tableau d'objets utilisateur |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -351,8 +354,8 @@ Créer un nouvel utilisateur dans Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'utilisateur créé |
|
||||
| `user` | object | Objet utilisateur créé |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -371,8 +374,8 @@ Créer plusieurs utilisateurs dans Zendesk en utilisant l'importation en masse
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de création en masse |
|
||||
| `jobStatus` | object | Objet d'état de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -399,8 +402,8 @@ Mettre à jour un utilisateur existant dans Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données utilisateur mises à jour |
|
||||
| `user` | object | Objet utilisateur mis à jour |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -419,8 +422,8 @@ Mettre à jour plusieurs utilisateurs dans Zendesk en utilisant la mise à jour
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de mise à jour en masse |
|
||||
| `jobStatus` | object | Objet d'état de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -439,8 +442,8 @@ Supprimer un utilisateur de Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'utilisateur supprimé |
|
||||
| `deleted` | boolean | Succès de la suppression |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -460,8 +463,9 @@ Récupérer une liste d'organisations depuis Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Données et métadonnées des organisations |
|
||||
| `organizations` | array | Tableau d'objets d'organisation |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -480,8 +484,8 @@ Obtenir une organisation unique par ID depuis Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Données de l'organisation |
|
||||
| `organization` | object | Objet organisation |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -502,8 +506,9 @@ Autocomplétion des organisations dans Zendesk par préfixe de nom (pour corresp
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Résultats de recherche des organisations |
|
||||
| `organizations` | array | Tableau d'objets d'organisation |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -527,8 +532,8 @@ Créer une nouvelle organisation dans Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'organisation créée |
|
||||
| `organization` | object | Objet organisation créé |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -547,8 +552,8 @@ Créer plusieurs organisations dans Zendesk en utilisant l'importation en masse
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Statut de la tâche de création en masse |
|
||||
| `jobStatus` | object | Objet statut de la tâche |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -573,8 +578,8 @@ Mettre à jour une organisation existante dans Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'organisation mise à jour |
|
||||
| `organization` | object | Objet organisation mis à jour |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -593,8 +598,8 @@ Supprimer une organisation de Zendesk
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données de l'organisation supprimée |
|
||||
| `deleted` | boolean | Succès de la suppression |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -617,8 +622,9 @@ Recherche unifiée à travers les tickets, utilisateurs et organisations dans Ze
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Résultats de recherche |
|
||||
| `results` | array | Tableau d'objets résultats |
|
||||
| `paging` | object | Informations de pagination |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -637,8 +643,8 @@ Compter le nombre de résultats de recherche correspondant à une requête dans
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Résultat du comptage de recherche |
|
||||
| `count` | number | Nombre de résultats correspondants |
|
||||
| `metadata` | object | Métadonnées de l'opération |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ Exa AIを使用してウェブを検索します。タイトル、URL、テキ
|
||||
| `type` | string | いいえ | 検索タイプ:neural、keyword、auto、またはfast(デフォルト:auto) |
|
||||
| `includeDomains` | string | いいえ | 結果に含めるドメインのカンマ区切りリスト |
|
||||
| `excludeDomains` | string | いいえ | 結果から除外するドメインのカンマ区切りリスト |
|
||||
| `category` | string | いいえ | カテゴリによるフィルタリング:company、research_paper、news_article、pdf、github、tweet、movie、song、personal_site |
|
||||
| `category` | string | いいえ | カテゴリによるフィルタリング:company、research paper、news、pdf、github、tweet、personal site、linkedin profile、financial report |
|
||||
| `text` | boolean | いいえ | 結果に全文コンテンツを含める(デフォルト:false) |
|
||||
| `highlights` | boolean | いいえ | 結果にハイライトされたスニペットを含める(デフォルト:false) |
|
||||
| `summary` | boolean | いいえ | 結果にAI生成の要約を含める(デフォルト:false) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:always、fallback、またはnever(デフォルト:never) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:never(デフォルト)、fallback、always、またはpreferred(常にライブクロールを試み、失敗した場合はキャッシュにフォールバック) |
|
||||
| `apiKey` | string | はい | Exa AI APIキー |
|
||||
|
||||
#### 出力
|
||||
@@ -68,12 +68,12 @@ Exa AIを使用してウェブページのコンテンツを取得します。
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `urls` | string | はい | コンテンツを取得するURLのカンマ区切りリスト |
|
||||
| `text` | boolean | いいえ | trueの場合、デフォルト設定でページ全文を返します。falseの場合、テキスト返却を無効にします。 |
|
||||
| `text` | boolean | いいえ | trueの場合、デフォルト設定で完全なページテキストを返します。falseの場合、テキスト返却を無効にします。 |
|
||||
| `summaryQuery` | string | いいえ | 要約生成をガイドするクエリ |
|
||||
| `subpages` | number | いいえ | 提供されたURLからクロールするサブページの数 |
|
||||
| `subpageTarget` | string | いいえ | 特定のサブページを対象とするカンマ区切りのキーワード(例:"docs,tutorial,about") |
|
||||
| `subpageTarget` | string | いいえ | 特定のサブページをターゲットにするためのカンマ区切りキーワード(例:"docs,tutorial,about") |
|
||||
| `highlights` | boolean | いいえ | 結果にハイライトされたスニペットを含める(デフォルト:false) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:always、fallback、またはnever(デフォルト:never) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:never(デフォルト)、fallback、always、またはpreferred(常にライブクロールを試み、失敗した場合はキャッシュにフォールバック) |
|
||||
| `apiKey` | string | はい | Exa AI APIキー |
|
||||
|
||||
#### 出力
|
||||
@@ -96,10 +96,9 @@ Exa AIを使用して、指定されたURLに類似したウェブページを
|
||||
| `includeDomains` | string | いいえ | 結果に含めるドメインのカンマ区切りリスト |
|
||||
| `excludeDomains` | string | いいえ | 結果から除外するドメインのカンマ区切りリスト |
|
||||
| `excludeSourceDomain` | boolean | いいえ | 結果からソースドメインを除外する(デフォルト:false) |
|
||||
| `category` | string | いいえ | カテゴリによるフィルタリング:company、research_paper、news_article、pdf、github、tweet、movie、song、personal_site |
|
||||
| `highlights` | boolean | いいえ | 結果にハイライトされたスニペットを含める(デフォルト:false) |
|
||||
| `summary` | boolean | いいえ | 結果にAI生成の要約を含める(デフォルト:false) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:always、fallback、またはnever(デフォルト:never) |
|
||||
| `livecrawl` | string | いいえ | ライブクロールモード:never(デフォルト)、fallback、always、またはpreferred(常にライブクロールを試み、失敗した場合はキャッシュにフォールバック) |
|
||||
| `apiKey` | string | はい | Exa AI APIキー |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -91,11 +91,11 @@ Parallel AIを使用してウェブ全体で包括的な詳細調査を実施し
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | タスクのステータス(実行中、完了、失敗) |
|
||||
| `status` | string | タスクのステータス(完了、失敗) |
|
||||
| `run_id` | string | この調査タスクの一意のID |
|
||||
| `message` | string | ステータスメッセージ(実行中のタスク用) |
|
||||
| `message` | string | ステータスメッセージ |
|
||||
| `content` | object | 調査結果(output_schemaに基づいて構造化) |
|
||||
| `basis` | array | 引用と情報源(抜粋と信頼度レベルを含む) |
|
||||
| `basis` | array | 引用と情報源(根拠と信頼度レベルを含む) |
|
||||
|
||||
## 注意事項
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ Perplexity AIチャットモデルを使用して文章を生成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作の成功ステータス |
|
||||
| `output` | object | チャット生成結果 |
|
||||
| `content` | string | 生成されたテキストコンテンツ |
|
||||
| `model` | string | 生成に使用されたモデル |
|
||||
| `usage` | object | トークン使用情報 |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -76,8 +77,7 @@ Perplexityからランク付けされた検索結果を取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作の成功ステータス |
|
||||
| `output` | object | 検索結果 |
|
||||
| `results` | array | 検索結果の配列 |
|
||||
|
||||
## 注意事項
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Simでは、思考ツールによってエージェントがこのような意
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | string | はい | 思考ステップブロックでユーザーが提供した思考プロセスまたは指示。 |
|
||||
| `thought` | string | はい | あなたの内部的な推論、分析、または思考プロセス。これを使用して、応答する前に問題を段階的に考えてください。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -31,39 +31,32 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
## ツール
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
サポートされている任意のLLMプロバイダーにチャット完了リクエストを送信する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | はい | 使用するモデル(例:gpt-4o、claude-sonnet-4-5、gemini-2.0-flash) |
|
||||
| `systemPrompt` | string | いいえ | アシスタントの動作を設定するシステムプロンプト |
|
||||
| `context` | string | はい | モデルに送信するユーザーメッセージまたはコンテキスト |
|
||||
| `apiKey` | string | いいえ | プロバイダーのAPIキー(ホストされたモデルの場合、提供されなければプラットフォームキーを使用) |
|
||||
| `temperature` | number | いいえ | レスポンス生成の温度(0-2) |
|
||||
| `maxTokens` | number | いいえ | レスポンスの最大トークン数 |
|
||||
| `azureEndpoint` | string | いいえ | Azure OpenAIエンドポイントURL |
|
||||
| `azureApiVersion` | string | いいえ | Azure OpenAI APIバージョン |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | 翻訳されたテキスト |
|
||||
| `model` | string | 使用されたモデル |
|
||||
| `tokens` | json | トークン使用量 |
|
||||
| `content` | string | 生成されたレスポンスの内容 |
|
||||
| `model` | string | 生成に使用されたモデル |
|
||||
| `tokens` | object | トークン使用情報 |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | 翻訳されたテキスト |
|
||||
| `model` | string | 使用されたモデル |
|
||||
| `tokens` | json | トークン使用量 |
|
||||
|
||||
## メモ
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- タイプ: `translate`
|
||||
|
||||
@@ -251,10 +251,10 @@ IDによってWordPress.comから単一のページを取得する
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WordPress.comのサイトIDまたはドメイン \(例:12345678またはmysite.wordpress.com\) |
|
||||
| `file` | string | はい | Base64エンコードされたファイルデータまたはファイルを取得するURL |
|
||||
| `filename` | string | はい | 拡張子付きのファイル名 \(例:image.jpg\) |
|
||||
| `title` | string | いいえ | メディアのタイトル |
|
||||
| `caption` | string | いいえ | メディアのキャプション |
|
||||
| `file` | file | いいえ | アップロードするファイル(UserFileオブジェクト) |
|
||||
| `filename` | string | いいえ | オプションのファイル名上書き(例:image.jpg) |
|
||||
| `title` | string | いいえ | メディアタイトル |
|
||||
| `caption` | string | いいえ | メディアキャプション |
|
||||
| `altText` | string | いいえ | アクセシビリティのための代替テキスト |
|
||||
| `description` | string | いいえ | メディアの説明 |
|
||||
|
||||
|
||||
@@ -170,25 +170,6 @@ YouTube再生リストから動画を取得します。
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | プレイリスト内の動画の配列 |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
特定のYouTube動画に関連する動画を検索します。
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | string | はい | 関連動画を検索するYouTube動画ID |
|
||||
| `maxResults` | number | いいえ | 返す関連動画の最大数(1-50) |
|
||||
| `pageToken` | string | いいえ | ページネーション用のページトークン |
|
||||
| `apiKey` | string | はい | YouTube APIキー |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | 関連動画の配列 |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
YouTube動画からコメントを取得します。
|
||||
@@ -199,7 +180,7 @@ YouTube動画からコメントを取得します。
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | string | はい | YouTube動画ID |
|
||||
| `maxResults` | number | いいえ | 返すコメントの最大数 |
|
||||
| `order` | string | いいえ | コメントの並び順:timeまたはrelevance |
|
||||
| `order` | string | いいえ | コメントの並び順:time(時間順)またはrelevance(関連性順) |
|
||||
| `pageToken` | string | いいえ | ページネーション用のページトークン |
|
||||
| `apiKey` | string | はい | YouTube APIキー |
|
||||
|
||||
@@ -211,5 +192,5 @@ YouTube動画からコメントを取得します。
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- カテゴリ: `tools`
|
||||
- タイプ: `youtube`
|
||||
|
||||
@@ -73,8 +73,9 @@ Zendeskをワークフローに統合します。チケットの取得、チケ
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | チケットデータとメタデータ |
|
||||
| `tickets` | array | チケットオブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -93,8 +94,8 @@ IDによってZendeskから単一のチケットを取得
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | チケットデータ |
|
||||
| `ticket` | object | チケットオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -122,8 +123,8 @@ IDによってZendeskから単一のチケットを取得
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 作成されたチケットデータ |
|
||||
| `ticket` | object | 作成されたチケットオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -142,8 +143,8 @@ Zendeskで一度に複数のチケットを作成(最大100件)
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 一括作成ジョブステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -171,8 +172,8 @@ Zendeskで一度に複数のチケットを作成(最大100件)
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 更新されたチケットデータ |
|
||||
| `ticket` | object | 更新されたチケットオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -196,8 +197,8 @@ Zendeskで複数のチケットを一度に更新(最大100件)
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 一括更新ジョブのステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -216,8 +217,8 @@ Zendeskからチケットを削除
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 削除確認 |
|
||||
| `deleted` | boolean | 削除成功 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -238,8 +239,8 @@ Zendeskからチケットを削除
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 統合ジョブステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -261,8 +262,9 @@ Zendeskからチケットを削除
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | ユーザーデータとメタデータ |
|
||||
| `users` | array | ユーザーオブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -281,8 +283,8 @@ ZendeskからIDで単一のユーザーを取得
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | ユーザーデータ |
|
||||
| `user` | object | ユーザーオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -300,8 +302,8 @@ Zendeskから現在認証されているユーザーを取得
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 現在のユーザーデータ |
|
||||
| `user` | object | 現在のユーザーオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -323,8 +325,9 @@ Zendeskから現在認証されているユーザーを取得
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | ユーザー検索結果 |
|
||||
| `users` | array | ユーザーオブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -350,8 +353,8 @@ Zendeskに新しいユーザーを作成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 作成されたユーザーデータ |
|
||||
| `user` | object | 作成されたユーザーオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -370,8 +373,8 @@ Zendeskに新しいユーザーを作成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 一括作成ジョブのステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -398,8 +401,8 @@ Zendeskの既存ユーザーを更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 更新されたユーザーデータ |
|
||||
| `user` | object | 更新されたユーザーオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -418,8 +421,8 @@ Zendeskの既存ユーザーを更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 一括更新ジョブステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -438,8 +441,8 @@ Zendeskからユーザーを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 削除されたユーザーデータ |
|
||||
| `deleted` | boolean | 削除成功 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -459,8 +462,9 @@ Zendeskから組織のリストを取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 組織データとメタデータ |
|
||||
| `organizations` | array | 組織オブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -479,8 +483,8 @@ ZendeskからIDで単一の組織を取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 組織データ |
|
||||
| `organization` | object | 組織オブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -501,8 +505,9 @@ ZendeskからIDで単一の組織を取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 組織検索結果 |
|
||||
| `organizations` | array | 組織オブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -526,8 +531,8 @@ Zendeskで新しい組織を作成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 作成された組織データ |
|
||||
| `organization` | object | 作成された組織オブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -546,8 +551,8 @@ Zendeskで新しい組織を作成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 一括作成ジョブステータス |
|
||||
| `jobStatus` | object | ジョブステータスオブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -572,8 +577,8 @@ Zendeskで既存の組織を更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 更新された組織データ |
|
||||
| `organization` | object | 更新された組織オブジェクト |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -592,8 +597,8 @@ Zendeskから組織を削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 削除された組織データ |
|
||||
| `deleted` | boolean | 削除成功 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -616,8 +621,9 @@ Zendeskでチケット、ユーザー、組織を横断した統合検索
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 検索結果 |
|
||||
| `results` | array | 結果オブジェクトの配列 |
|
||||
| `paging` | object | ページネーション情報 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -636,8 +642,8 @@ Zendeskでクエリに一致する検索結果の数をカウント
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 検索カウント結果 |
|
||||
| `count` | number | 一致する結果の数 |
|
||||
| `metadata` | object | 操作メタデータ |
|
||||
|
||||
## 注意事項
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `type` | string | 否 | 搜索类型:neural、keyword、auto 或 fast \(默认值: auto\) |
|
||||
| `includeDomains` | string | 否 | 用逗号分隔的域名列表,包含在结果中 |
|
||||
| `excludeDomains` | string | 否 | 用逗号分隔的域名列表,从结果中排除 |
|
||||
| `category` | string | 否 | 按类别筛选:company、research_paper、news_article、pdf、github、tweet、movie、song、personal_site |
|
||||
| `category` | string | 否 | 按类别筛选:company、research paper、news、pdf、github、tweet、personal site、linkedin profile、financial report |
|
||||
| `text` | boolean | 否 | 在结果中包含完整文本内容 \(默认值: false\) |
|
||||
| `highlights` | boolean | 否 | 在结果中包含高亮片段 \(默认值: false\) |
|
||||
| `summary` | boolean | 否 | 在结果中包含 AI 生成的摘要 \(默认值: false\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:always、fallback 或 never \(默认值: never\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:never \(默认值\)、fallback、always 或 preferred \(始终尝试实时爬取,失败时回退到缓存\) |
|
||||
| `apiKey` | string | 是 | Exa AI API 密钥 |
|
||||
|
||||
#### 输出
|
||||
@@ -67,13 +67,13 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `urls` | string | 是 | 用逗号分隔的 URL 列表,用于获取内容 |
|
||||
| `text` | boolean | 否 | 如果为 true,则返回默认设置的完整页面文本。如果为 false,则禁用文本返回。 |
|
||||
| `urls` | string | 是 | 用逗号分隔的 URL 列表,用于检索内容 |
|
||||
| `text` | boolean | 否 | 如果为 true,则返回具有默认设置的完整页面文本。如果为 false,则禁用文本返回。 |
|
||||
| `summaryQuery` | string | 否 | 用于指导摘要生成的查询 |
|
||||
| `subpages` | number | 否 | 从提供的 URL 爬取的子页面数量 |
|
||||
| `subpages` | number | 否 | 从提供的 URL 中爬取的子页面数量 |
|
||||
| `subpageTarget` | string | 否 | 用逗号分隔的关键字,用于定位特定子页面 \(例如:"docs,tutorial,about"\) |
|
||||
| `highlights` | boolean | 否 | 在结果中包含高亮片段 \(默认值: false\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:always、fallback 或 never \(默认值: never\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:never \(默认值\)、fallback、always 或 preferred \(始终尝试实时爬取,失败时回退到缓存\) |
|
||||
| `apiKey` | string | 是 | Exa AI API 密钥 |
|
||||
|
||||
#### 输出
|
||||
@@ -90,16 +90,15 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `url` | string | 是 | 要查找相似链接的 URL |
|
||||
| `url` | string | 是 | 用于查找相似链接的 URL |
|
||||
| `numResults` | number | 否 | 要返回的相似链接数量 \(默认值: 10,最大值: 25\) |
|
||||
| `text` | boolean | 否 | 是否包含相似页面的完整文本 |
|
||||
| `includeDomains` | string | 否 | 用逗号分隔的域名列表,包含在结果中 |
|
||||
| `excludeDomains` | string | 否 | 用逗号分隔的域名列表,从结果中排除 |
|
||||
| `excludeSourceDomain` | boolean | 否 | 从结果中排除源域名 \(默认值: false\) |
|
||||
| `category` | string | 否 | 按类别筛选:company、research_paper、news_article、pdf、github、tweet、movie、song、personal_site |
|
||||
| `highlights` | boolean | 否 | 在结果中包含高亮片段 \(默认值: false\) |
|
||||
| `summary` | boolean | 否 | 在结果中包含 AI 生成的摘要 \(默认值: false\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:always、fallback 或 never \(默认值: never\) |
|
||||
| `livecrawl` | string | 否 | 实时爬取模式:never \(默认值\), fallback, always, 或 preferred \(始终尝试实时爬取,失败时回退到缓存\) |
|
||||
| `apiKey` | string | 是 | Exa AI API 密钥 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -91,11 +91,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | 任务状态(运行中、已完成、失败) |
|
||||
| `status` | string | 任务状态(已完成,失败) |
|
||||
| `run_id` | string | 此研究任务的唯一 ID |
|
||||
| `message` | string | 状态消息(针对运行中的任务) |
|
||||
| `message` | string | 状态消息 |
|
||||
| `content` | object | 研究结果(基于 output_schema 结构化) |
|
||||
| `basis` | array | 引用和来源,包括摘录和置信度等级 |
|
||||
| `basis` | array | 引用和来源,包括推理和置信度等级 |
|
||||
|
||||
## 注意
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 聊天补全结果 |
|
||||
| `content` | string | 生成的文本内容 |
|
||||
| `model` | string | 用于生成的模型 |
|
||||
| `usage` | object | 令牌使用信息 |
|
||||
|
||||
### `perplexity_search`
|
||||
|
||||
@@ -76,8 +77,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 搜索结果 |
|
||||
| `results` | array | 搜索结果数组 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | 字符串 | 是 | 用户在思考步骤模块中提供的思考过程或指令。 |
|
||||
| `thought` | 字符串 | 是 | 您的内部推理、分析或思考过程。使用此项逐步思考问题,然后再作出回应。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -31,39 +31,32 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
## 工具
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
向任何支持的 LLM 提供商发送聊天完成请求
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | 是 | 要使用的模型 \(例如,gpt-4o、claude-sonnet-4-5、gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | 否 | 设置助手行为的系统提示 |
|
||||
| `context` | string | 是 | 要发送给模型的用户消息或上下文 |
|
||||
| `apiKey` | string | 否 | 提供商的 API 密钥 \(如果未为托管模型提供,则使用平台密钥\) |
|
||||
| `temperature` | number | 否 | 响应生成的温度 \(0-2\) |
|
||||
| `maxTokens` | number | 否 | 响应的最大令牌数 |
|
||||
| `azureEndpoint` | string | 否 | Azure OpenAI 端点 URL |
|
||||
| `azureApiVersion` | string | 否 | Azure OpenAI API 版本 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | 翻译后的文本 |
|
||||
| `model` | string | 使用的模型 |
|
||||
| `tokens` | json | 令牌使用情况 |
|
||||
| `content` | string | 生成的响应内容 |
|
||||
| `model` | string | 用于生成的模型 |
|
||||
| `tokens` | object | 令牌使用信息 |
|
||||
|
||||
### `anthropic_chat`
|
||||
## 注意
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | 翻译后的文本 |
|
||||
| `model` | string | 使用的模型 |
|
||||
| `tokens` | json | Token 使用情况 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 类别:`tools`
|
||||
- 类型:`translate`
|
||||
- 类别: `tools`
|
||||
- 类型: `translate`
|
||||
|
||||
@@ -250,13 +250,13 @@ Sim 与 WordPress 的集成让您的代理可以自动化处理重要的网站
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | 字符串 | 是 | WordPress.com 站点 ID 或域名(例如:12345678 或 mysite.wordpress.com) |
|
||||
| `file` | 字符串 | 是 | Base64 编码的文件数据或用于获取文件的 URL |
|
||||
| `filename` | 字符串 | 是 | 带扩展名的文件名(例如:image.jpg) |
|
||||
| `title` | 字符串 | 否 | 媒体标题 |
|
||||
| `caption` | 字符串 | 否 | 媒体说明 |
|
||||
| `altText` | 字符串 | 否 | 用于无障碍的替代文本 |
|
||||
| `description` | 字符串 | 否 | 媒体描述 |
|
||||
| `siteId` | string | 是 | WordPress.com 站点 ID 或域名 \(例如:12345678 或 mysite.wordpress.com\) |
|
||||
| `file` | file | 否 | 要上传的文件 \(UserFile 对象\) |
|
||||
| `filename` | string | 否 | 可选的文件名覆盖 \(例如:image.jpg\) |
|
||||
| `title` | string | 否 | 媒体标题 |
|
||||
| `caption` | string | 否 | 媒体说明 |
|
||||
| `altText` | string | 否 | 用于无障碍的替代文本 |
|
||||
| `description` | string | 否 | 媒体描述 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -170,28 +170,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | 数组 | 播放列表中的视频数组 |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
查找与特定 YouTube 视频相关的视频。
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | 字符串 | 是 | 用于查找相关视频的 YouTube 视频 ID |
|
||||
| `maxResults` | 数字 | 否 | 返回相关视频的最大数量 \(1-50\) |
|
||||
| `pageToken` | 字符串 | 否 | 分页的页面令牌 |
|
||||
| `apiKey` | 字符串 | 是 | YouTube API 密钥 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | 数组 | 相关视频的数组 |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
从 YouTube 视频中获取评论。
|
||||
从 YouTube 视频获取评论。
|
||||
|
||||
#### 输入
|
||||
|
||||
@@ -207,7 +188,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | 数组 | 视频评论的数组 |
|
||||
| `items` | 数组 | 视频中的评论数组 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 工单数据和元数据 |
|
||||
| `tickets` | array | 工单对象数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -93,8 +94,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 工单数据 |
|
||||
| `ticket` | object | 工单对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -122,8 +123,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 创建的工单数据 |
|
||||
| `ticket` | object | 创建的工单对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -142,8 +143,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 批量创建任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -171,8 +172,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 更新的工单数据 |
|
||||
| `ticket` | object | 更新的工单对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -196,8 +197,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 批量更新任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -216,8 +217,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 删除确认 |
|
||||
| `deleted` | boolean | 删除成功 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -238,8 +239,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 合并任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -261,8 +262,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 用户数据和元数据 |
|
||||
| `users` | array | 用户对象数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -281,8 +283,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 用户数据 |
|
||||
| `user` | object | 用户对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -300,8 +302,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 当前用户数据 |
|
||||
| `user` | object | 当前用户对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -323,8 +325,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 用户搜索结果 |
|
||||
| `users` | array | 用户对象数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -350,8 +353,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 创建的用户数据 |
|
||||
| `user` | object | 创建的用户对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -370,8 +373,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 批量创建任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -398,8 +401,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 更新的用户数据 |
|
||||
| `user` | object | 更新的用户对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -418,8 +421,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 批量更新任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -438,8 +441,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 已删除的用户数据 |
|
||||
| `deleted` | boolean | 删除成功 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -459,8 +462,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 组织数据和元数据 |
|
||||
| `organizations` | array | 组织对象数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -479,8 +483,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 组织数据 |
|
||||
| `organization` | object | 组织对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -501,8 +505,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 组织搜索结果 |
|
||||
| `organizations` | array | 组织对象数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -526,8 +531,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 创建的组织数据 |
|
||||
| `organization` | object | 创建的组织对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -546,8 +551,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 批量创建任务状态 |
|
||||
| `jobStatus` | object | 任务状态对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -572,8 +577,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 更新后的组织数据 |
|
||||
| `organization` | object | 更新的组织对象 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -592,8 +597,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 已删除的组织数据 |
|
||||
| `deleted` | boolean | 删除成功 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -616,8 +621,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 搜索结果 |
|
||||
| `results` | array | 结果对象的数组 |
|
||||
| `paging` | object | 分页信息 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -636,8 +642,8 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | object | 搜索计数结果 |
|
||||
| `count` | number | 匹配结果的数量 |
|
||||
| `metadata` | object | 操作元数据 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
|
||||
@@ -278,20 +278,14 @@ checksums:
|
||||
content/42: 135fd99536465a8de4cd18eae682128f
|
||||
content/43: bcadfc362b69078beee0088e5936c98b
|
||||
content/44: e4824dd7acff8ac2bba07075c1bf5555
|
||||
content/45: 09cf2346cf5ce107fae6c9f05ff79340
|
||||
content/46: 00c6430ff11748e620cfe9309f9b76d0
|
||||
content/45: 29d730395e60843523997a5440265c2b
|
||||
content/46: f40cf8dcdbca5bdabddd2ccafe510827
|
||||
content/47: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/48: a57680d79a2db023cabd03d74a357037
|
||||
content/48: fb04fb808287bc585c7bef1622cc5929
|
||||
content/49: bcadfc362b69078beee0088e5936c98b
|
||||
content/50: f189d5068833ff066248bdec22ac14da
|
||||
content/51: 29d730395e60843523997a5440265c2b
|
||||
content/52: f40cf8dcdbca5bdabddd2ccafe510827
|
||||
content/53: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/54: fb04fb808287bc585c7bef1622cc5929
|
||||
content/55: bcadfc362b69078beee0088e5936c98b
|
||||
content/56: 766eb78674c8d8a40cfd13eca533b7a4
|
||||
content/57: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/58: 9f8b3ebb53dbde8abfb139b4a8bc0396
|
||||
content/50: 766eb78674c8d8a40cfd13eca533b7a4
|
||||
content/51: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/52: 9f8b3ebb53dbde8abfb139b4a8bc0396
|
||||
31b5f846eb7f4b1751b4bd9233cc3e12:
|
||||
meta/title: dc96f9cb0985d77c30f55927723f0175
|
||||
meta/description: 15c7d592f04655f534458cd82180ba1f
|
||||
@@ -560,19 +554,14 @@ checksums:
|
||||
content/5: 821e6394b0a953e2b0842b04ae8f3105
|
||||
content/6: 3e7ad55e438e18098a384877ec5d62e7
|
||||
content/7: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/8: ad28bec163e3e627e8741423286840bd
|
||||
content/9: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/10: d71b6bb8e2dd6ce98101aec6a1dd77f2
|
||||
content/11: bcadfc362b69078beee0088e5936c98b
|
||||
content/12: adefb6abb806004812f51485919b8bf5
|
||||
content/13: 6f45ac035872e548e43385c22ddac507
|
||||
content/14: 8e7b622079252125355db846fbde9d96
|
||||
content/15: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/16: d71b6bb8e2dd6ce98101aec6a1dd77f2
|
||||
content/17: bcadfc362b69078beee0088e5936c98b
|
||||
content/18: adefb6abb806004812f51485919b8bf5
|
||||
content/19: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/20: 2bcdb5144797b6a7dd2d266331c9b95e
|
||||
content/8: 6325adefb6e1520835225285b18b6a45
|
||||
content/9: b7fa85fce9c7476fe132df189e27dac1
|
||||
content/10: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/11: 985f435f721b00df4d13fa0a5552684c
|
||||
content/12: bcadfc362b69078beee0088e5936c98b
|
||||
content/13: 6af66efd0da20944a87fdb8d9defa358
|
||||
content/14: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/15: 2bcdb5144797b6a7dd2d266331c9b95e
|
||||
403c3b5c22a4dfef9c7dcfe7e5668348:
|
||||
meta/title: 6ea728a87df2a68e40938bba653845d2
|
||||
meta/description: 19eaaa8cac2b54c15a1c3f7d61b52457
|
||||
@@ -588,7 +577,7 @@ checksums:
|
||||
content/9: 55d3faa55bd37eb2f4ab74379410a5a9
|
||||
content/10: b52a2ab3e371e2a6eca0d16c4b628f69
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: 870dadb88971694d23e19c63009e16e0
|
||||
content/12: f629a1a44ad4cf0d2dd3eb43e8bdf5f9
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: 0fd095f0d3f37eac2be2be452b3ba1dc
|
||||
content/15: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
@@ -1316,13 +1305,13 @@ checksums:
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: 6abb969d0ca1807d6401266ab8f50975
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: 8e69b5b320bb75e70df03437e894c776
|
||||
content/14: 57008e827f0df4dc136962228c1c74a7
|
||||
content/15: 3a3c262e00210fad2ff657eaf5aac97d
|
||||
content/16: 10de36438cb6e1cec1535bee5cb73699
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: 0bd5204d7085a5b9dcfc82e1b3ed2714
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: d84fb23e5dfc9d41a177acd7dfb28e72
|
||||
content/20: fbab8b16c84eb1264e43e99da04006bd
|
||||
content/21: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/22: 568b0cef19d3e81ea300a5621ad42ab5
|
||||
25de8a658947e92bca61370185aeda9d:
|
||||
@@ -1354,7 +1343,7 @@ checksums:
|
||||
content/23: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/24: 5a56ae7f7ba1a4385da43f822782b024
|
||||
content/25: bcadfc362b69078beee0088e5936c98b
|
||||
content/26: 98c3ad979dfde09dc41d1b6e370b38f5
|
||||
content/26: 2f75132ca9307c0a90456a238e9a742b
|
||||
content/27: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/28: 04857d373a5405ac83582efac37e1dc1
|
||||
153fdbf063881f22370488d6b1b177be:
|
||||
@@ -3513,19 +3502,19 @@ checksums:
|
||||
content/9: b012197e65487ff167f2cbd78da85eac
|
||||
content/10: 0cc2f207ef4e2f3ac0acf4cf9b3aba2c
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: 10980279b69aa6c6a1464f263ca25be6
|
||||
content/12: 989c1aa1d50cf098f58aa7d1589f05dd
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: 3d15775cf6e0105686ea02f90008b64b
|
||||
content/15: 4499d456d0b0166932b1cdcc55c21396
|
||||
content/16: 58f73a8f3dfd938f5d211a9ce5df6770
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: cbf55c968ed5f0917b7269bf1eaa6afc
|
||||
content/18: f1ad77da5d66a7bae33ec46c56b95ddf
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: e04f86af43c8bdc3e61d3935ba9e85fb
|
||||
content/21: 9c18dfa51781c1e30cb2e2741b92b091
|
||||
content/22: 8f86d82c91437004767ce62918f5ede6
|
||||
content/23: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/24: be02ad818111831d726d6bca32c59fe7
|
||||
content/24: e73c4bd6da7dbf85f5af136d078d0500
|
||||
content/25: bcadfc362b69078beee0088e5936c98b
|
||||
content/26: 9176672ef538cc624ee574133f9949e7
|
||||
content/27: c9afca4e81299f2f696c4e988878b18d
|
||||
@@ -46278,157 +46267,157 @@ checksums:
|
||||
content/14: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/15: 03790a503987168076530036c1493fe5
|
||||
content/16: bcadfc362b69078beee0088e5936c98b
|
||||
content/17: 54e111a2489eba7bdff7b7fe4376b8a7
|
||||
content/17: ea45046a2ff2b4b0d87c76fc6b97c408
|
||||
content/18: 52c12f35ed540fb4095071b8febadbd6
|
||||
content/19: 8b22b32d6e35d60ce06e26b8cdde7516
|
||||
content/20: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/21: dea74d6e978d7a0cb2eb9d17c0d75743
|
||||
content/22: bcadfc362b69078beee0088e5936c98b
|
||||
content/23: 1b166ea32dff5f8de92b256fe48200d7
|
||||
content/23: b99563e729dbe27993fdb4d95a46d8d5
|
||||
content/24: 2e0c17af85edfb251596a7774ebc148f
|
||||
content/25: f062fa5ca051bc3f3d208c8932508fc0
|
||||
content/26: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/27: 43a06769b0b005d1e7d9028fc4e53665
|
||||
content/28: bcadfc362b69078beee0088e5936c98b
|
||||
content/29: 573530e346d195727862b03b380f40fc
|
||||
content/29: 72c013f869e9976ad77f19655a1e8c08
|
||||
content/30: 720be6a9405a4d31e16d25dc262eddb6
|
||||
content/31: 0e47014b2ea303913c7129b3e4da290a
|
||||
content/32: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/33: 490e114043914c0dd0b41a8a9eaa4d9d
|
||||
content/34: bcadfc362b69078beee0088e5936c98b
|
||||
content/35: 2aa59cb206803bd55848d1dbc4f02c24
|
||||
content/35: 6e083e5204c044c175986bc4de65142f
|
||||
content/36: afab481f543682e3c895d06bffff104b
|
||||
content/37: 8f7732c5ac7c54dced63abc5531979f5
|
||||
content/38: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/39: a1b3b3d3a6ae1ab701d5a138a65268e9
|
||||
content/40: bcadfc362b69078beee0088e5936c98b
|
||||
content/41: 209979bf1ba673df324a3e121a11c8a3
|
||||
content/41: 3cfb80c9ee50d78855f92714695fd099
|
||||
content/42: 963255b39dc080d1e7293b700e84614c
|
||||
content/43: 3423e945033f3bae0146a6909ceedc8f
|
||||
content/44: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/45: d41d7acef728fbaaec3398c4ff7b8fbe
|
||||
content/46: bcadfc362b69078beee0088e5936c98b
|
||||
content/47: cf3e596e8dd672a8d8d4857f51e9ab50
|
||||
content/47: 6e083e5204c044c175986bc4de65142f
|
||||
content/48: 9ab9aad5aa1e726435361084e272a014
|
||||
content/49: 1d3de7001c97111aeb341e2fb5ec815a
|
||||
content/50: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/51: 1b1a3fbf365b1595c261fb87488cb2ec
|
||||
content/52: bcadfc362b69078beee0088e5936c98b
|
||||
content/53: aa539c46a35e99f7440fa5c0d394c2db
|
||||
content/53: 3386c2debe2e941da427f5c1904fa767
|
||||
content/54: 60112dd6b483b56f99f4c16c9884b050
|
||||
content/55: 547bcbaeb75e52e529f102fd008326af
|
||||
content/56: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/57: aba9802d4a2ab9b4515aa98efcabb951
|
||||
content/58: bcadfc362b69078beee0088e5936c98b
|
||||
content/59: 42baf3d6d8998277e787dff34d0c9205
|
||||
content/59: 6e083e5204c044c175986bc4de65142f
|
||||
content/60: 78b20224accffac2854e9a92729474ec
|
||||
content/61: 060052f51f91b3181ec5161788f7b8ca
|
||||
content/62: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/63: bd6bc4020abf3ef5f6e3e680d5b28a14
|
||||
content/64: bcadfc362b69078beee0088e5936c98b
|
||||
content/65: 1b12c9e7e17dc8a545788c0e59039e85
|
||||
content/65: cf03036e119b398bf5187e8289f3f94f
|
||||
content/66: 9f56a114c0953177aed24db130781e6a
|
||||
content/67: f6cfb33fdbaa58daf786ba40f7c0a7c4
|
||||
content/68: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/69: 3b3d202acc98df0cf6b79e902aae2d7e
|
||||
content/70: bcadfc362b69078beee0088e5936c98b
|
||||
content/71: e57935e8dc8c529b7cd6d1bbd625c571
|
||||
content/71: 2665d3aed70b1659e3b6e14310ff6ac3
|
||||
content/72: 57a45f869a822311c41280e85410615f
|
||||
content/73: 28a8643f2c30a6e2af1209e7c04af013
|
||||
content/74: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/75: 927aaa7ff75ed5d79f338dab1b65a5d4
|
||||
content/76: bcadfc362b69078beee0088e5936c98b
|
||||
content/77: b920a5d942663ab36af04d7588767c22
|
||||
content/77: 81ad1310cb520e2d27438070e9336192
|
||||
content/78: 7d4b7f84297208d69bf1e76e5261862a
|
||||
content/79: f9849f3e5c1253f99871a4a306a1d989
|
||||
content/80: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/81: c1b61507ca0c8558ae4b63c1e816bb47
|
||||
content/82: bcadfc362b69078beee0088e5936c98b
|
||||
content/83: 1a337e24a2607d14708da299915edc97
|
||||
content/83: cf03036e119b398bf5187e8289f3f94f
|
||||
content/84: 636045dc118e3ba21f9d39376650122e
|
||||
content/85: 97b39365b3102e15acac67ca2dbdda19
|
||||
content/86: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/87: e36155b52053b654d4d37e51bb4942e6
|
||||
content/88: bcadfc362b69078beee0088e5936c98b
|
||||
content/89: c9e060877ac3730e4402be772ac46ce8
|
||||
content/89: 5ca2489579987217db1a2c875b96d9f9
|
||||
content/90: 1432d33da621fc006a55d406d02b9a37
|
||||
content/91: fc8958b8145693dba3f21b6927623f6e
|
||||
content/92: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/93: 0ceb2a6d432abc7ae35c5d702efd6138
|
||||
content/94: bcadfc362b69078beee0088e5936c98b
|
||||
content/95: 74ae0903eb838fb659541a8db6e8fa03
|
||||
content/95: 6e083e5204c044c175986bc4de65142f
|
||||
content/96: d278aa56d3be3d2573356cc6408ba158
|
||||
content/97: 99a1c61fceaa1bc28862fac36ce2f4e7
|
||||
content/98: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/99: 10f26827078049449566a718edaabcde
|
||||
content/100: bcadfc362b69078beee0088e5936c98b
|
||||
content/101: a50beaa2074bd143cd62b7fc23554f60
|
||||
content/101: 1b4d8e47d7af5392c3b2d26ff2ded123
|
||||
content/102: 9375b46687d820ca8bf3f4cee903569f
|
||||
content/103: 8adabd834d3e643b995fa5e0bcaaa91e
|
||||
content/104: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/105: e4be303f29ceeae8431191379edf2299
|
||||
content/106: bcadfc362b69078beee0088e5936c98b
|
||||
content/107: cf3e596e8dd672a8d8d4857f51e9ab50
|
||||
content/107: 6e083e5204c044c175986bc4de65142f
|
||||
content/108: 75ebf9b0cd4b860936f60f62bba9b01d
|
||||
content/109: ec54d03fd37c2acf17f0b6ecc27e0e5c
|
||||
content/110: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/111: c36524c25d5e4d2180b2466c21444be3
|
||||
content/112: bcadfc362b69078beee0088e5936c98b
|
||||
content/113: cec912475a9662b72d3266e24ccfeabf
|
||||
content/113: 3386c2debe2e941da427f5c1904fa767
|
||||
content/114: 9bf8d3e3fadd33bf0d1a3b2f1c191d76
|
||||
content/115: 04c526cf6db7ae45bd6b69f4fbe3cec0
|
||||
content/116: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/117: fd010a6f907ab6d156bcc8942c7d8fab
|
||||
content/118: bcadfc362b69078beee0088e5936c98b
|
||||
content/119: c46faeb199b61577a56855249a26ed15
|
||||
content/119: 28e1ea07f7ed31c219d900a239011abc
|
||||
content/120: d11520ec23cfe64d97182999af637729
|
||||
content/121: 90b9a292124bf143507cedaea38afac8
|
||||
content/122: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/123: b674e049685fa8b815185f8b0999148b
|
||||
content/124: bcadfc362b69078beee0088e5936c98b
|
||||
content/125: ba0b2310309adab963b529e3beb0c1b2
|
||||
content/125: 3701c897476e40630b0c21084e502926
|
||||
content/126: 401e619e5f586d7a98cb40919eb4e8c0
|
||||
content/127: cd2944a378bdf25f5195d0213a92e5c5
|
||||
content/128: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/129: 69176bce8f2277c0aab5ae5cb26036b9
|
||||
content/130: bcadfc362b69078beee0088e5936c98b
|
||||
content/131: 5ccf80dc8b3aa024fc35aefcfd7cb271
|
||||
content/131: 28e1ea07f7ed31c219d900a239011abc
|
||||
content/132: 741bf0308b6d0a358c53114cab40393a
|
||||
content/133: 248f9e5380cbdd539a3e18722d72bcd1
|
||||
content/134: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/135: c9ad4274c5941afc9ec3949a42c511b2
|
||||
content/136: bcadfc362b69078beee0088e5936c98b
|
||||
content/137: cae631cdfc9299fd10c6ed5cf956df77
|
||||
content/137: 3238a9fa1c649af888db930ff0181397
|
||||
content/138: 88092a1d9ebf0b96cc2263677ab7875e
|
||||
content/139: 74e37cd1cf3f31a08e874966f08618e9
|
||||
content/140: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/141: 3c2f509845fd8ebd43e2469f231b5ec6
|
||||
content/142: bcadfc362b69078beee0088e5936c98b
|
||||
content/143: 74ae0903eb838fb659541a8db6e8fa03
|
||||
content/143: 6e083e5204c044c175986bc4de65142f
|
||||
content/144: 3fa05da9a789cbe306e6e37532ea5b20
|
||||
content/145: beea05186900898272601f78c7b4ffbc
|
||||
content/146: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/147: 1c20c4e275db53d81e1a26d5be248df1
|
||||
content/148: bcadfc362b69078beee0088e5936c98b
|
||||
content/149: caa918a2c7e8f3f319dc5dde1f40e064
|
||||
content/149: 8ba2ff0291d55fa722dc1ce0f00bf90c
|
||||
content/150: d697e1dc249684ff4628ab7d847ebeec
|
||||
content/151: 6530f207edacc508f1fffed44c6e3eea
|
||||
content/152: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/153: 8abbb8779b9abb22a555a7529dde3c21
|
||||
content/154: bcadfc362b69078beee0088e5936c98b
|
||||
content/155: c1dab1cc1a29f4fc13632d7616e8e1f6
|
||||
content/155: 3386c2debe2e941da427f5c1904fa767
|
||||
content/156: a6ac06461462f71313b9a69d33c4713f
|
||||
content/157: b83a3e027493b33566e157eae7b067bb
|
||||
content/158: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/159: 654d2d9f89b93a4f46b846930b957f81
|
||||
content/160: bcadfc362b69078beee0088e5936c98b
|
||||
content/161: d84fb23e5dfc9d41a177acd7dfb28e72
|
||||
content/161: bb4850d44287327333b0400dd7aa6bd9
|
||||
content/162: 5e43194e8a59fd28d7c47b73f4c21c0a
|
||||
content/163: 862627f89b796dd70b796b7a56989f02
|
||||
content/164: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/165: 0590e327298a51c83d743ac718562a6a
|
||||
content/166: bcadfc362b69078beee0088e5936c98b
|
||||
content/167: 9b8ed8eaf45789402878e0c9b179f4ad
|
||||
content/167: 0b9e2bbe5ee526600c866f3c8305dc87
|
||||
content/168: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/169: 47663eb519251c4ba49564d0e7c36e94
|
||||
41b4c06bb56053083938bcf6d958856d:
|
||||
@@ -47761,7 +47750,7 @@ checksums:
|
||||
content/68: ada572e729e01d615e2bed27d33796cb
|
||||
content/69: b45b536bf6f2547c9058fca134946283
|
||||
content/70: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/71: a7ca3f5cb26e88ffcbb6972d13b8e0ab
|
||||
content/71: 8b0984caf1aa8d793736f1c56b8c5e9a
|
||||
content/72: bcadfc362b69078beee0088e5936c98b
|
||||
content/73: 820449909a823294cef60f274eb7dd70
|
||||
content/74: d676ef2d8dca4c76ca28bac77c6e83cb
|
||||
|
||||
@@ -3,7 +3,6 @@ import { userStats } from '@sim/db/schema'
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { deductFromCredits } from '@/lib/billing/credits/balance'
|
||||
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
|
||||
import { checkInternalApiKey } from '@/lib/copilot/utils'
|
||||
import { isBillingEnabled } from '@/lib/core/config/environment'
|
||||
@@ -92,17 +91,11 @@ export async function POST(req: NextRequest) {
|
||||
return NextResponse.json({ error: 'User stats record not found' }, { status: 500 })
|
||||
}
|
||||
|
||||
const { creditsUsed, overflow } = await deductFromCredits(userId, cost)
|
||||
if (creditsUsed > 0) {
|
||||
logger.info(`[${requestId}] Deducted cost from credits`, { userId, creditsUsed, overflow })
|
||||
}
|
||||
const costToStore = overflow
|
||||
|
||||
const updateFields = {
|
||||
totalCost: sql`total_cost + ${costToStore}`,
|
||||
currentPeriodCost: sql`current_period_cost + ${costToStore}`,
|
||||
totalCopilotCost: sql`total_copilot_cost + ${costToStore}`,
|
||||
currentPeriodCopilotCost: sql`current_period_copilot_cost + ${costToStore}`,
|
||||
totalCost: sql`total_cost + ${cost}`,
|
||||
currentPeriodCost: sql`current_period_cost + ${cost}`,
|
||||
totalCopilotCost: sql`total_copilot_cost + ${cost}`,
|
||||
currentPeriodCopilotCost: sql`current_period_copilot_cost + ${cost}`,
|
||||
totalCopilotCalls: sql`total_copilot_calls + 1`,
|
||||
lastActive: new Date(),
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'path'
|
||||
import binaryExtensionsList from 'binary-extensions'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { validateExternalUrl } from '@/lib/core/security/input-validation'
|
||||
import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation'
|
||||
import { isSupportedFileType, parseFile } from '@/lib/file-parsers'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { isUsingCloudStorage, type StorageContext, StorageService } from '@/lib/uploads'
|
||||
@@ -270,7 +270,7 @@ async function handleExternalUrl(
|
||||
logger.info('Fetching external URL:', url)
|
||||
logger.info('WorkspaceId for URL save:', workspaceId)
|
||||
|
||||
const urlValidation = validateExternalUrl(url, 'fileUrl')
|
||||
const urlValidation = await validateUrlWithDNS(url, 'fileUrl')
|
||||
if (!urlValidation.isValid) {
|
||||
logger.warn(`Blocked external URL request: ${urlValidation.error}`)
|
||||
return {
|
||||
@@ -346,8 +346,12 @@ async function handleExternalUrl(
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
const pinnedUrl = createPinnedUrl(url, urlValidation.resolvedIP!)
|
||||
const response = await fetch(pinnedUrl, {
|
||||
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),
|
||||
headers: {
|
||||
Host: urlValidation.originalHostname!,
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { db } from '@sim/db'
|
||||
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
|
||||
import {
|
||||
permissions,
|
||||
workflow,
|
||||
workflowDeploymentVersion,
|
||||
workflowExecutionLogs,
|
||||
} from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
@@ -29,6 +34,7 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
|
||||
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
|
||||
level: workflowExecutionLogs.level,
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
@@ -46,9 +52,15 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
|
||||
workflowWorkspaceId: workflow.workspaceId,
|
||||
workflowCreatedAt: workflow.createdAt,
|
||||
workflowUpdatedAt: workflow.updatedAt,
|
||||
deploymentVersion: workflowDeploymentVersion.version,
|
||||
deploymentVersionName: workflowDeploymentVersion.name,
|
||||
})
|
||||
.from(workflowExecutionLogs)
|
||||
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
|
||||
.leftJoin(
|
||||
workflowDeploymentVersion,
|
||||
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
|
||||
)
|
||||
.innerJoin(
|
||||
permissions,
|
||||
and(
|
||||
@@ -81,6 +93,9 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
|
||||
id: log.id,
|
||||
workflowId: log.workflowId,
|
||||
executionId: log.executionId,
|
||||
deploymentVersionId: log.deploymentVersionId,
|
||||
deploymentVersion: log.deploymentVersion ?? null,
|
||||
deploymentVersionName: log.deploymentVersionName ?? null,
|
||||
level: log.level,
|
||||
duration: log.totalDurationMs ? `${log.totalDurationMs}ms` : null,
|
||||
trigger: log.trigger,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { db } from '@sim/db'
|
||||
import { pausedExecutions, permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
|
||||
import {
|
||||
pausedExecutions,
|
||||
permissions,
|
||||
workflow,
|
||||
workflowDeploymentVersion,
|
||||
workflowExecutionLogs,
|
||||
} from '@sim/db/schema'
|
||||
import { and, desc, eq, gte, inArray, isNotNull, isNull, lte, or, type SQL, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -51,6 +57,7 @@ export async function GET(request: NextRequest) {
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
|
||||
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
|
||||
level: workflowExecutionLogs.level,
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
@@ -71,6 +78,8 @@ export async function GET(request: NextRequest) {
|
||||
pausedStatus: pausedExecutions.status,
|
||||
pausedTotalPauseCount: pausedExecutions.totalPauseCount,
|
||||
pausedResumedCount: pausedExecutions.resumedCount,
|
||||
deploymentVersion: workflowDeploymentVersion.version,
|
||||
deploymentVersionName: workflowDeploymentVersion.name,
|
||||
}
|
||||
: {
|
||||
// Basic mode - exclude large fields for better performance
|
||||
@@ -78,6 +87,7 @@ export async function GET(request: NextRequest) {
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
|
||||
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
|
||||
level: workflowExecutionLogs.level,
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
@@ -98,6 +108,8 @@ export async function GET(request: NextRequest) {
|
||||
pausedStatus: pausedExecutions.status,
|
||||
pausedTotalPauseCount: pausedExecutions.totalPauseCount,
|
||||
pausedResumedCount: pausedExecutions.resumedCount,
|
||||
deploymentVersion: workflowDeploymentVersion.version,
|
||||
deploymentVersionName: sql<null>`NULL`, // Only needed in full mode for details panel
|
||||
}
|
||||
|
||||
const baseQuery = db
|
||||
@@ -107,6 +119,10 @@ export async function GET(request: NextRequest) {
|
||||
pausedExecutions,
|
||||
eq(pausedExecutions.executionId, workflowExecutionLogs.executionId)
|
||||
)
|
||||
.leftJoin(
|
||||
workflowDeploymentVersion,
|
||||
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
|
||||
)
|
||||
.innerJoin(
|
||||
workflow,
|
||||
and(
|
||||
@@ -397,6 +413,9 @@ export async function GET(request: NextRequest) {
|
||||
id: log.id,
|
||||
workflowId: log.workflowId,
|
||||
executionId: log.executionId,
|
||||
deploymentVersionId: log.deploymentVersionId,
|
||||
deploymentVersion: log.deploymentVersion ?? null,
|
||||
deploymentVersionName: log.deploymentVersionName ?? null,
|
||||
level: log.level,
|
||||
duration: log.totalDurationMs ? `${log.totalDurationMs}ms` : null,
|
||||
trigger: log.trigger,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateInternalToken } from '@/lib/auth/internal'
|
||||
import { isDev } from '@/lib/core/config/environment'
|
||||
import { validateProxyUrl } from '@/lib/core/security/input-validation'
|
||||
import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -173,7 +173,7 @@ export async function GET(request: Request) {
|
||||
return createErrorResponse("Missing 'url' parameter", 400)
|
||||
}
|
||||
|
||||
const urlValidation = validateProxyUrl(targetUrl)
|
||||
const urlValidation = await validateUrlWithDNS(targetUrl)
|
||||
if (!urlValidation.isValid) {
|
||||
logger.warn(`[${requestId}] Blocked proxy request`, {
|
||||
url: targetUrl.substring(0, 100),
|
||||
@@ -211,11 +211,13 @@ export async function GET(request: Request) {
|
||||
logger.info(`[${requestId}] Proxying ${method} request to: ${targetUrl}`)
|
||||
|
||||
try {
|
||||
const response = await fetch(targetUrl, {
|
||||
const pinnedUrl = createPinnedUrl(targetUrl, urlValidation.resolvedIP!)
|
||||
const response = await fetch(pinnedUrl, {
|
||||
method: method,
|
||||
headers: {
|
||||
...getProxyHeaders(),
|
||||
...customHeaders,
|
||||
Host: urlValidation.originalHostname!,
|
||||
},
|
||||
body: body || undefined,
|
||||
})
|
||||
|
||||
@@ -227,16 +227,16 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
logger.info(`[${requestId}] Transcription completed successfully`)
|
||||
|
||||
return NextResponse.json({
|
||||
transcript,
|
||||
segments,
|
||||
language: detectedLanguage,
|
||||
duration,
|
||||
confidence,
|
||||
sentiment: sentimentResults,
|
||||
entities,
|
||||
summary,
|
||||
})
|
||||
const response: Record<string, any> = { transcript }
|
||||
if (segments !== undefined) response.segments = segments
|
||||
if (detectedLanguage !== undefined) response.language = detectedLanguage
|
||||
if (duration !== undefined) response.duration = duration
|
||||
if (confidence !== undefined) response.confidence = confidence
|
||||
if (sentimentResults !== undefined) response.sentiment = sentimentResults
|
||||
if (entities !== undefined) response.entities = entities
|
||||
if (summary !== undefined) response.summary = summary
|
||||
|
||||
return NextResponse.json(response)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] STT proxy error:`, error)
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
||||
@@ -277,11 +277,11 @@ async function transcribeWithWhisper(
|
||||
formData.append('temperature', temperature.toString())
|
||||
}
|
||||
|
||||
formData.append('response_format', 'verbose_json')
|
||||
|
||||
if (timestamps === 'word') {
|
||||
formData.append('response_format', 'verbose_json')
|
||||
formData.append('timestamp_granularities', 'word')
|
||||
} else if (timestamps === 'sentence') {
|
||||
formData.append('response_format', 'verbose_json')
|
||||
formData.append('timestamp_granularities', 'segment')
|
||||
}
|
||||
|
||||
@@ -302,17 +302,14 @@ async function transcribeWithWhisper(
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (timestamps === 'none') {
|
||||
return {
|
||||
transcript: data.text,
|
||||
language: data.language,
|
||||
}
|
||||
let segments: TranscriptSegment[] | undefined
|
||||
if (timestamps !== 'none') {
|
||||
segments = (data.segments || data.words || []).map((seg: any) => ({
|
||||
text: seg.text,
|
||||
start: seg.start,
|
||||
end: seg.end,
|
||||
}))
|
||||
}
|
||||
const segments: TranscriptSegment[] = (data.segments || data.words || []).map((seg: any) => ({
|
||||
text: seg.text,
|
||||
start: seg.start,
|
||||
end: seg.end,
|
||||
}))
|
||||
|
||||
return {
|
||||
transcript: data.text,
|
||||
|
||||
@@ -19,7 +19,6 @@ export async function createMySQLConnection(config: MySQLConnectionConfig) {
|
||||
}
|
||||
|
||||
if (config.ssl === 'disabled') {
|
||||
// Don't set ssl property at all to disable SSL
|
||||
} else if (config.ssl === 'required') {
|
||||
connectionConfig.ssl = { rejectUnauthorized: true }
|
||||
} else if (config.ssl === 'preferred') {
|
||||
@@ -54,42 +53,6 @@ export async function executeQuery(
|
||||
export function validateQuery(query: string): { isValid: boolean; error?: string } {
|
||||
const trimmedQuery = query.trim().toLowerCase()
|
||||
|
||||
const dangerousPatterns = [
|
||||
/drop\s+database/i,
|
||||
/drop\s+schema/i,
|
||||
/drop\s+user/i,
|
||||
/create\s+user/i,
|
||||
/grant\s+/i,
|
||||
/revoke\s+/i,
|
||||
/alter\s+user/i,
|
||||
/set\s+global/i,
|
||||
/set\s+session/i,
|
||||
/load\s+data/i,
|
||||
/into\s+outfile/i,
|
||||
/into\s+dumpfile/i,
|
||||
/load_file\s*\(/i,
|
||||
/system\s+/i,
|
||||
/exec\s+/i,
|
||||
/execute\s+immediate/i,
|
||||
/xp_cmdshell/i,
|
||||
/sp_configure/i,
|
||||
/information_schema\.tables/i,
|
||||
/mysql\.user/i,
|
||||
/mysql\.db/i,
|
||||
/mysql\.host/i,
|
||||
/performance_schema/i,
|
||||
/sys\./i,
|
||||
]
|
||||
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (pattern.test(query)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Query contains potentially dangerous operation: ${pattern.source}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedStatements = /^(select|insert|update|delete|with|show|describe|explain)\s+/i
|
||||
if (!allowedStatements.test(trimmedQuery)) {
|
||||
return {
|
||||
|
||||
@@ -30,10 +30,7 @@ export async function createNeo4jDriver(config: Neo4jConnectionConfig) {
|
||||
return driver
|
||||
}
|
||||
|
||||
export function validateCypherQuery(
|
||||
query: string,
|
||||
allowDangerousOps = false
|
||||
): { isValid: boolean; error?: string } {
|
||||
export function validateCypherQuery(query: string): { isValid: boolean; error?: string } {
|
||||
if (!query || typeof query !== 'string') {
|
||||
return {
|
||||
isValid: false,
|
||||
@@ -41,33 +38,6 @@ export function validateCypherQuery(
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowDangerousOps) {
|
||||
const dangerousPatterns = [
|
||||
/DROP\s+DATABASE/i,
|
||||
/DROP\s+CONSTRAINT/i,
|
||||
/DROP\s+INDEX/i,
|
||||
/CREATE\s+DATABASE/i,
|
||||
/CREATE\s+CONSTRAINT/i,
|
||||
/CREATE\s+INDEX/i,
|
||||
/CALL\s+dbms\./i,
|
||||
/CALL\s+db\./i,
|
||||
/LOAD\s+CSV/i,
|
||||
/apoc\.cypher\.run/i,
|
||||
/apoc\.load/i,
|
||||
/apoc\.periodic/i,
|
||||
]
|
||||
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (pattern.test(query)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error:
|
||||
'Query contains potentially dangerous operations (schema changes, system procedures, or external data loading)',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedQuery = query.trim()
|
||||
if (trimmedQuery.length === 0) {
|
||||
return {
|
||||
|
||||
@@ -33,55 +33,16 @@ export async function executeQuery(
|
||||
params: unknown[] = []
|
||||
): Promise<{ rows: unknown[]; rowCount: number }> {
|
||||
const result = await sql.unsafe(query, params)
|
||||
const rowCount = result.count ?? result.length ?? 0
|
||||
return {
|
||||
rows: Array.isArray(result) ? result : [result],
|
||||
rowCount: Array.isArray(result) ? result.length : result ? 1 : 0,
|
||||
rowCount,
|
||||
}
|
||||
}
|
||||
|
||||
export function validateQuery(query: string): { isValid: boolean; error?: string } {
|
||||
const trimmedQuery = query.trim().toLowerCase()
|
||||
|
||||
// Block dangerous SQL operations
|
||||
const dangerousPatterns = [
|
||||
/drop\s+database/i,
|
||||
/drop\s+schema/i,
|
||||
/drop\s+user/i,
|
||||
/create\s+user/i,
|
||||
/create\s+role/i,
|
||||
/grant\s+/i,
|
||||
/revoke\s+/i,
|
||||
/alter\s+user/i,
|
||||
/alter\s+role/i,
|
||||
/set\s+role/i,
|
||||
/reset\s+role/i,
|
||||
/copy\s+.*from/i,
|
||||
/copy\s+.*to/i,
|
||||
/lo_import/i,
|
||||
/lo_export/i,
|
||||
/pg_read_file/i,
|
||||
/pg_write_file/i,
|
||||
/pg_ls_dir/i,
|
||||
/information_schema\.tables/i,
|
||||
/pg_catalog/i,
|
||||
/pg_user/i,
|
||||
/pg_shadow/i,
|
||||
/pg_roles/i,
|
||||
/pg_authid/i,
|
||||
/pg_stat_activity/i,
|
||||
/dblink/i,
|
||||
/\\\\copy/i,
|
||||
]
|
||||
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (pattern.test(query)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Query contains potentially dangerous operation: ${pattern.source}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedStatements = /^(select|insert|update|delete|with|explain|analyze|show)\s+/i
|
||||
if (!allowedStatements.test(trimmedQuery)) {
|
||||
return {
|
||||
@@ -147,9 +108,10 @@ export async function executeInsert(
|
||||
const query = `INSERT INTO ${sanitizedTable} (${sanitizedColumns.join(', ')}) VALUES (${placeholders.join(', ')}) RETURNING *`
|
||||
const result = await sql.unsafe(query, values)
|
||||
|
||||
const rowCount = result.count ?? result.length ?? 0
|
||||
return {
|
||||
rows: Array.isArray(result) ? result : [result],
|
||||
rowCount: Array.isArray(result) ? result.length : result ? 1 : 0,
|
||||
rowCount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +132,10 @@ export async function executeUpdate(
|
||||
const query = `UPDATE ${sanitizedTable} SET ${setClause} WHERE ${where} RETURNING *`
|
||||
const result = await sql.unsafe(query, values)
|
||||
|
||||
const rowCount = result.count ?? result.length ?? 0
|
||||
return {
|
||||
rows: Array.isArray(result) ? result : [result],
|
||||
rowCount: Array.isArray(result) ? result.length : result ? 1 : 0,
|
||||
rowCount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,8 +150,9 @@ export async function executeDelete(
|
||||
const query = `DELETE FROM ${sanitizedTable} WHERE ${where} RETURNING *`
|
||||
const result = await sql.unsafe(query, [])
|
||||
|
||||
const rowCount = result.count ?? result.length ?? 0
|
||||
return {
|
||||
rows: Array.isArray(result) ? result : [result],
|
||||
rowCount: Array.isArray(result) ? result.length : result ? 1 : 0,
|
||||
rowCount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
logger.info(`[${requestId}] Executing RDS query on ${params.database}`)
|
||||
|
||||
// Validate the query
|
||||
const validation = validateQuery(params.query)
|
||||
if (!validation.isValid) {
|
||||
logger.warn(`[${requestId}] Query validation failed: ${validation.error}`)
|
||||
|
||||
@@ -82,29 +82,6 @@ function parseFieldValue(field: Field): unknown {
|
||||
export function validateQuery(query: string): { isValid: boolean; error?: string } {
|
||||
const trimmedQuery = query.trim().toLowerCase()
|
||||
|
||||
const dangerousPatterns = [
|
||||
/drop\s+database/i,
|
||||
/drop\s+schema/i,
|
||||
/drop\s+user/i,
|
||||
/create\s+user/i,
|
||||
/create\s+role/i,
|
||||
/grant\s+/i,
|
||||
/revoke\s+/i,
|
||||
/alter\s+user/i,
|
||||
/alter\s+role/i,
|
||||
/set\s+role/i,
|
||||
/reset\s+role/i,
|
||||
]
|
||||
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (pattern.test(query)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Query contains potentially dangerous operation: ${pattern.source}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedStatements = /^(select|insert|update|delete|with|explain|show)\s+/i
|
||||
if (!allowedStatements.test(trimmedQuery)) {
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils'
|
||||
|
||||
const logger = createLogger('StagehandAgentAPI')
|
||||
|
||||
// Environment variables for Browserbase
|
||||
const BROWSERBASE_API_KEY = env.BROWSERBASE_API_KEY
|
||||
const BROWSERBASE_PROJECT_ID = env.BROWSERBASE_PROJECT_ID
|
||||
|
||||
@@ -44,9 +43,7 @@ function extractActionDirectives(task: string): {
|
||||
let match
|
||||
let processedTask = task
|
||||
|
||||
// Find all action directives in the task
|
||||
while ((match = actionRegex.exec(task)) !== null) {
|
||||
const _fullMatch = match[0]
|
||||
const actionText = match[1].trim()
|
||||
const index = match.index
|
||||
|
||||
@@ -56,8 +53,6 @@ function extractActionDirectives(task: string): {
|
||||
})
|
||||
}
|
||||
|
||||
// Replace action directives with placeholders for the agent
|
||||
// We'll number them to make it clear to the agent what's happening
|
||||
if (actionDirectives.length > 0) {
|
||||
let offset = 0
|
||||
for (let i = 0; i < actionDirectives.length; i++) {
|
||||
@@ -65,22 +60,18 @@ function extractActionDirectives(task: string): {
|
||||
const originalIndex = directive.index
|
||||
const placeholder = `[SECURE ACTION ${i + 1}]`
|
||||
|
||||
// Calculate position considering previous replacements
|
||||
const adjustedIndex = originalIndex - offset
|
||||
|
||||
// Get text to replace
|
||||
const fullMatch = task.substring(
|
||||
originalIndex,
|
||||
originalIndex + task.substring(originalIndex).indexOf(']]') + 2
|
||||
)
|
||||
|
||||
// Replace in processed task
|
||||
processedTask =
|
||||
processedTask.substring(0, adjustedIndex) +
|
||||
placeholder +
|
||||
processedTask.substring(adjustedIndex + fullMatch.length)
|
||||
|
||||
// Update offset for next replacement
|
||||
offset += fullMatch.length - placeholder.length
|
||||
}
|
||||
}
|
||||
@@ -88,7 +79,6 @@ function extractActionDirectives(task: string): {
|
||||
return { processedTask, actionDirectives }
|
||||
}
|
||||
|
||||
// Function to process secure actions in a given message
|
||||
async function processSecureActions(
|
||||
message: string,
|
||||
stagehand: Stagehand,
|
||||
@@ -98,21 +88,17 @@ async function processSecureActions(
|
||||
modifiedMessage: string
|
||||
executedActions: Array<{ action: string; result: { success: boolean; message: string } }>
|
||||
}> {
|
||||
// Track executed actions and modified message
|
||||
const executedActions: Array<{ action: string; result: { success: boolean; message: string } }> =
|
||||
[]
|
||||
let modifiedMessage = message
|
||||
|
||||
// Look for secure action execute directives
|
||||
const secureActionMatches = [...message.matchAll(/EXECUTE SECURE ACTION (\d+)/gi)]
|
||||
|
||||
// Process each secure action request
|
||||
for (const match of secureActionMatches) {
|
||||
const fullMatch = match[0]
|
||||
const actionIndex = Number.parseInt(match[1], 10) - 1 // Convert to 0-based index
|
||||
const actionIndex = Number.parseInt(match[1], 10) - 1
|
||||
|
||||
if (actionDirectives[actionIndex]) {
|
||||
// Found a secure action directive to execute
|
||||
const actionDirective = actionDirectives[actionIndex]
|
||||
let resultMessage = ''
|
||||
|
||||
@@ -121,14 +107,10 @@ async function processSecureActions(
|
||||
action: actionDirective.action,
|
||||
})
|
||||
|
||||
// Perform the action with variable substitution at runtime
|
||||
// This uses act() directly, which handles variables securely
|
||||
const result = await stagehand.page.act({
|
||||
action: actionDirective.action,
|
||||
const result = await stagehand.act(actionDirective.action, {
|
||||
variables: variables || {},
|
||||
})
|
||||
|
||||
// Store the result for later reporting
|
||||
executedActions.push({
|
||||
action: actionDirective.action,
|
||||
result: {
|
||||
@@ -137,7 +119,6 @@ async function processSecureActions(
|
||||
},
|
||||
})
|
||||
|
||||
// Success message to replace the execution request
|
||||
resultMessage = `\nSecure action ${actionIndex + 1} executed successfully.\n`
|
||||
} catch (error) {
|
||||
logger.error(`Error executing secure action ${actionIndex + 1}`, {
|
||||
@@ -145,7 +126,6 @@ async function processSecureActions(
|
||||
action: actionDirective.action,
|
||||
})
|
||||
|
||||
// Store the failed result
|
||||
executedActions.push({
|
||||
action: actionDirective.action,
|
||||
result: {
|
||||
@@ -154,14 +134,11 @@ async function processSecureActions(
|
||||
},
|
||||
})
|
||||
|
||||
// Error message to replace the execution request
|
||||
resultMessage = `\nError executing secure action ${actionIndex + 1}: ${error instanceof Error ? error.message : 'Unknown error'}\n`
|
||||
}
|
||||
|
||||
// Replace the execution directive with the result message
|
||||
modifiedMessage = modifiedMessage.replace(fullMatch, resultMessage)
|
||||
} else {
|
||||
// Invalid action index - replace with error message
|
||||
const errorMessage = `\nError: Secure action ${actionIndex + 1} does not exist.\n`
|
||||
modifiedMessage = modifiedMessage.replace(fullMatch, errorMessage)
|
||||
}
|
||||
@@ -170,7 +147,6 @@ async function processSecureActions(
|
||||
return { modifiedMessage, executedActions }
|
||||
}
|
||||
|
||||
// Helper function for direct login attempt
|
||||
async function attemptDirectLogin(
|
||||
stagehand: Stagehand,
|
||||
variables: Record<string, string> | undefined
|
||||
@@ -187,11 +163,9 @@ async function attemptDirectLogin(
|
||||
}
|
||||
}
|
||||
|
||||
// Define common variable keys for credentials
|
||||
const usernameKeys = ['username', 'email', 'user']
|
||||
const passwordKeys = ['password', 'pass', 'secret']
|
||||
|
||||
// Find the actual keys used in the variables
|
||||
const usernameKey = usernameKeys.find((key) => variables[key] !== undefined)
|
||||
const passwordKey = passwordKeys.find((key) => variables[key] !== undefined)
|
||||
|
||||
@@ -210,9 +184,8 @@ async function attemptDirectLogin(
|
||||
logger.info('Attempting direct login with provided variables.')
|
||||
|
||||
try {
|
||||
const page = stagehand.page
|
||||
const page = stagehand.context.pages()[0]
|
||||
|
||||
// Common selectors for username/email fields
|
||||
const usernameSelectors = [
|
||||
'input[type="text"][name*="user"]',
|
||||
'input[type="email"]',
|
||||
@@ -225,7 +198,6 @@ async function attemptDirectLogin(
|
||||
'input[aria-label*="email" i]',
|
||||
]
|
||||
|
||||
// Common selectors for password fields
|
||||
const passwordSelectors = [
|
||||
'input[type="password"]',
|
||||
'input[name*="pass"]',
|
||||
@@ -234,7 +206,6 @@ async function attemptDirectLogin(
|
||||
'input[aria-label*="pass" i]',
|
||||
]
|
||||
|
||||
// Common selectors for submit buttons
|
||||
const submitSelectors = [
|
||||
'button[type="submit"]',
|
||||
'input[type="submit"]',
|
||||
@@ -246,12 +217,10 @@ async function attemptDirectLogin(
|
||||
'button[name*="submit"]',
|
||||
]
|
||||
|
||||
// Find and fill username
|
||||
let usernameFilled = false
|
||||
for (const selector of usernameSelectors) {
|
||||
const input = page.locator(selector).first()
|
||||
if ((await input.count()) > 0 && (await input.isVisible({ timeout: 1000 }))) {
|
||||
// Short timeout
|
||||
if ((await input.count()) > 0 && (await input.isVisible())) {
|
||||
logger.info(`Found username field: ${selector}`)
|
||||
await input.fill(usernameValue)
|
||||
usernameFilled = true
|
||||
@@ -268,11 +237,10 @@ async function attemptDirectLogin(
|
||||
}
|
||||
}
|
||||
|
||||
// Find and fill password
|
||||
let passwordFilled = false
|
||||
for (const selector of passwordSelectors) {
|
||||
const input = page.locator(selector).first()
|
||||
if ((await input.count()) > 0 && (await input.isVisible({ timeout: 1000 }))) {
|
||||
if ((await input.count()) > 0 && (await input.isVisible())) {
|
||||
logger.info(`Found password field: ${selector}`)
|
||||
await input.fill(passwordValue)
|
||||
passwordFilled = true
|
||||
@@ -282,7 +250,6 @@ async function attemptDirectLogin(
|
||||
|
||||
if (!passwordFilled) {
|
||||
logger.warn('Could not find a visible password field for direct login.')
|
||||
// Even if password field not found, maybe username submit works? Unlikely but possible.
|
||||
return {
|
||||
attempted: true,
|
||||
success: false,
|
||||
@@ -291,20 +258,13 @@ async function attemptDirectLogin(
|
||||
}
|
||||
}
|
||||
|
||||
// Find and click submit button
|
||||
let submitClicked = false
|
||||
for (const selector of submitSelectors) {
|
||||
const button = page.locator(selector).first()
|
||||
// Check if button exists and is visible/enabled
|
||||
if (
|
||||
(await button.count()) > 0 &&
|
||||
(await button.isVisible({ timeout: 1000 })) &&
|
||||
(await button.isEnabled({ timeout: 1000 }))
|
||||
) {
|
||||
if ((await button.count()) > 0 && (await button.isVisible())) {
|
||||
logger.info(`Found submit button: ${selector}`)
|
||||
await button.click()
|
||||
// Wait longer for login processing
|
||||
await page.waitForTimeout(3000)
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000))
|
||||
submitClicked = true
|
||||
break
|
||||
}
|
||||
@@ -324,8 +284,6 @@ async function attemptDirectLogin(
|
||||
'Direct login attempt completed (fields filled, submit clicked). Verifying result...'
|
||||
)
|
||||
|
||||
// Verify if login was successful by checking for common success indicators
|
||||
// 1. Check if we're redirected away from login page
|
||||
const currentUrl = page.url()
|
||||
const isStillOnLoginPage =
|
||||
currentUrl.includes('login') ||
|
||||
@@ -334,9 +292,7 @@ async function attemptDirectLogin(
|
||||
currentUrl.includes('signup') ||
|
||||
currentUrl.includes('register')
|
||||
|
||||
// 2. Check for login error messages
|
||||
const hasLoginError = await page.evaluate(() => {
|
||||
// Look for common error message elements
|
||||
const errorSelectors = [
|
||||
'[class*="error" i]',
|
||||
'[id*="error" i]',
|
||||
@@ -367,9 +323,7 @@ async function attemptDirectLogin(
|
||||
return false
|
||||
})
|
||||
|
||||
// 3. Check for common success indicators
|
||||
const hasSuccessIndicators = await page.evaluate(() => {
|
||||
// Check for user menu elements, profile info, etc.
|
||||
const userMenuSelectors = [
|
||||
'[class*="avatar" i]',
|
||||
'[class*="profile" i]',
|
||||
@@ -454,26 +408,19 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
const params = validationResult.data
|
||||
// Simplify variable handling - safely convert any format to the object we need
|
||||
let variablesObject: Record<string, string> | undefined
|
||||
|
||||
// Handle different formats of variables that might come from the UI
|
||||
if (params.variables) {
|
||||
if (Array.isArray(params.variables)) {
|
||||
// For array format (from table input)
|
||||
variablesObject = {}
|
||||
params.variables.forEach((item: any) => {
|
||||
// Check if item and item.cells exist, and Key is a string
|
||||
if (item?.cells?.Key && typeof item.cells.Key === 'string') {
|
||||
// Access Key and Value within the 'cells' object
|
||||
variablesObject![item.cells.Key] = item.cells.Value || ''
|
||||
}
|
||||
})
|
||||
} else if (typeof params.variables === 'object' && params.variables !== null) {
|
||||
// For object format (already in correct format)
|
||||
variablesObject = { ...params.variables }
|
||||
} else if (typeof params.variables === 'string') {
|
||||
// Handle string format (sometimes comes as JSON string)
|
||||
try {
|
||||
variablesObject = JSON.parse(params.variables)
|
||||
} catch (_e) {
|
||||
@@ -481,14 +428,12 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we have non-empty variables
|
||||
if (!variablesObject || Object.keys(variablesObject).length === 0) {
|
||||
logger.warn('Variables object is empty after processing', {
|
||||
originalVariables: params.variables,
|
||||
variablesType: typeof params.variables,
|
||||
})
|
||||
|
||||
// Try to recover username/password from the raw variables if we can
|
||||
if (typeof params.variables === 'object' && params.variables !== null) {
|
||||
variablesObject = {}
|
||||
for (const key in params.variables) {
|
||||
@@ -502,7 +447,6 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Log the collected variables (careful not to log actual passwords)
|
||||
if (variablesObject) {
|
||||
const safeVarKeys = Object.keys(variablesObject).map((key) => {
|
||||
return key.toLowerCase().includes('password')
|
||||
@@ -519,10 +463,8 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const { task, startUrl: rawStartUrl, outputSchema, apiKey } = params
|
||||
|
||||
// Normalize the starting URL - only add https:// if needed
|
||||
let startUrl = rawStartUrl
|
||||
|
||||
// Add https:// if no protocol is specified
|
||||
startUrl = normalizeUrl(startUrl)
|
||||
|
||||
logger.info('Starting Stagehand agent process', {
|
||||
@@ -532,7 +474,6 @@ export async function POST(request: NextRequest) {
|
||||
hasVariables: !!variablesObject && Object.keys(variablesObject).length > 0,
|
||||
})
|
||||
|
||||
// Check for required environment variables
|
||||
if (!BROWSERBASE_API_KEY || !BROWSERBASE_PROJECT_ID) {
|
||||
logger.error('Missing required environment variables', {
|
||||
hasBrowserbaseApiKey: !!BROWSERBASE_API_KEY,
|
||||
@@ -546,68 +487,41 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
try {
|
||||
// Initialize Stagehand with Browserbase
|
||||
logger.info('Initializing Stagehand with Browserbase')
|
||||
logger.info('Initializing Stagehand with Browserbase (v3)')
|
||||
|
||||
stagehand = new Stagehand({
|
||||
env: 'BROWSERBASE',
|
||||
apiKey: BROWSERBASE_API_KEY,
|
||||
projectId: BROWSERBASE_PROJECT_ID,
|
||||
verbose: 1,
|
||||
// Use a custom logger wrapper that adapts our logger to Stagehand's expected format
|
||||
logger: (msg) => logger.info(typeof msg === 'string' ? msg : JSON.stringify(msg)),
|
||||
disablePino: true,
|
||||
modelName: 'claude-3-7-sonnet-20250219',
|
||||
modelClientOptions: {
|
||||
apiKey: apiKey, // User's OpenAI API key
|
||||
model: {
|
||||
modelName: 'anthropic/claude-3-7-sonnet-latest',
|
||||
apiKey: apiKey,
|
||||
},
|
||||
})
|
||||
|
||||
// Initialize Stagehand
|
||||
logger.info('Starting stagehand.init()')
|
||||
await stagehand.init()
|
||||
logger.info('Stagehand initialized successfully')
|
||||
|
||||
// Monkey patch the page.act method to automatically apply variables to all actions
|
||||
if (variablesObject && Object.keys(variablesObject).length > 0) {
|
||||
logger.info('Setting up automatic variable substitution for all actions')
|
||||
const originalAct = stagehand.page.act.bind(stagehand.page)
|
||||
stagehand.page.act = async (options: any) => {
|
||||
// If options is a string, convert it to object
|
||||
if (typeof options === 'string') {
|
||||
options = { action: options }
|
||||
}
|
||||
const page = stagehand.context.pages()[0]
|
||||
|
||||
// Ensure variables are included
|
||||
options.variables = { ...(options.variables || {}), ...variablesObject }
|
||||
|
||||
logger.info('Executing act with variables', {
|
||||
action: options.action,
|
||||
hasVariables: true,
|
||||
variableCount: Object.keys(options.variables).length,
|
||||
})
|
||||
|
||||
// Call original method
|
||||
return originalAct(options)
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to the start URL
|
||||
logger.info(`Navigating to ${startUrl}`)
|
||||
await stagehand.page.goto(startUrl, { waitUntil: 'networkidle' })
|
||||
await page.goto(startUrl, { waitUntil: 'networkidle' })
|
||||
logger.info('Navigation complete')
|
||||
|
||||
// Helper function to detect and navigate to login page if needed
|
||||
const ensureLoginPage = async (): Promise<boolean> => {
|
||||
if (!stagehand) {
|
||||
logger.error('Stagehand instance is null')
|
||||
return false
|
||||
}
|
||||
|
||||
const currentPage = stagehand.context.pages()[0]
|
||||
logger.info('Checking if we need to navigate to login page')
|
||||
|
||||
try {
|
||||
// Check if we're already on a page with login form
|
||||
const loginFormExists = await stagehand.page.evaluate(() => {
|
||||
const loginFormExists = await currentPage.evaluate(() => {
|
||||
const usernameInput = document.querySelector(
|
||||
'input[type="text"], input[type="email"], input[name="username"], input[id="username"]'
|
||||
)
|
||||
@@ -620,10 +534,7 @@ export async function POST(request: NextRequest) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Look for common login buttons/links
|
||||
const loginElements = await stagehand.page.observe({
|
||||
instruction: 'Find login buttons or links on this page',
|
||||
})
|
||||
const loginElements = await stagehand.observe('Find login buttons or links on this page')
|
||||
|
||||
if (loginElements && loginElements.length > 0) {
|
||||
for (const element of loginElements) {
|
||||
@@ -633,18 +544,13 @@ export async function POST(request: NextRequest) {
|
||||
) {
|
||||
logger.info(`Found login element: ${element.description}`)
|
||||
|
||||
// Click the login button/link
|
||||
if (element.selector) {
|
||||
logger.info(`Clicking login element: ${element.selector}`)
|
||||
await stagehand.page.act({
|
||||
action: `Click on the ${element.description}`,
|
||||
})
|
||||
await stagehand.act(`Click on the ${element.description}`)
|
||||
|
||||
// Wait for navigation or DOM changes
|
||||
await stagehand.page.waitForTimeout(2000)
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
|
||||
// Check if we're now on login page
|
||||
const loginPageAfterClick = await stagehand.page.evaluate(() => {
|
||||
const loginPageAfterClick = await currentPage.evaluate(() => {
|
||||
const usernameInput = document.querySelector(
|
||||
'input[type="text"], input[type="email"], input[name="username"], input[id="username"]'
|
||||
)
|
||||
@@ -661,15 +567,13 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Try direct navigation to /login if we couldn't find login elements
|
||||
logger.info('Trying direct navigation to /login path')
|
||||
const currentUrl = await stagehand.page.url()
|
||||
const currentUrl = currentPage.url()
|
||||
const loginUrl = new URL('/login', currentUrl).toString()
|
||||
|
||||
await stagehand.page.goto(loginUrl, { waitUntil: 'networkidle' })
|
||||
await currentPage.goto(loginUrl, { waitUntil: 'networkidle' })
|
||||
|
||||
// Check if we're now on login page
|
||||
const loginPageAfterDirectNav = await stagehand.page.evaluate(() => {
|
||||
const loginPageAfterDirectNav = await currentPage.evaluate(() => {
|
||||
const usernameInput = document.querySelector(
|
||||
'input[type="text"], input[type="email"], input[name="username"], input[id="username"]'
|
||||
)
|
||||
@@ -690,14 +594,12 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Direct Login Logic ---
|
||||
let directLoginAttempted = false
|
||||
let directLoginSuccess = false
|
||||
let loginMessage = ''
|
||||
let taskForAgent = task // Use original task by default
|
||||
let agentInstructions = '' // Will be generated below
|
||||
let taskForAgent = task
|
||||
let agentInstructions = ''
|
||||
|
||||
// Only attempt direct login if relevant variables exist
|
||||
const hasLoginVars =
|
||||
variablesObject &&
|
||||
Object.keys(variablesObject).some((k) =>
|
||||
@@ -725,7 +627,6 @@ export async function POST(request: NextRequest) {
|
||||
})
|
||||
|
||||
if (directLoginAttempted) {
|
||||
// Modify task for agent regardless of login success/failure
|
||||
if (directLoginSuccess) {
|
||||
taskForAgent = `Login has been completed programmatically and was successful. Please verify that you are logged in and then proceed with the original task: ${task}`
|
||||
} else {
|
||||
@@ -741,19 +642,15 @@ export async function POST(request: NextRequest) {
|
||||
} else {
|
||||
logger.info('Skipping direct login: No relevant username/password variables found.')
|
||||
}
|
||||
// --- End Direct Login Logic ---
|
||||
|
||||
// Extract action directives with variable placeholders (from original task)
|
||||
const { processedTask, actionDirectives } = extractActionDirectives(task) // Use original task for extraction
|
||||
const { processedTask, actionDirectives } = extractActionDirectives(task)
|
||||
|
||||
logger.info('Extracted action directives', {
|
||||
actionCount: actionDirectives.length,
|
||||
hasActionDirectives: actionDirectives.length > 0,
|
||||
})
|
||||
|
||||
// Generate instructions based on whether direct login was attempted
|
||||
if (directLoginAttempted) {
|
||||
// Construct specific instructions based on login attempt outcome
|
||||
const loginInstructions = directLoginSuccess
|
||||
? 'Login was completed programmatically and appears successful. Please VERIFY if the login was successful by checking for elements that only appear when logged in.'
|
||||
: `Login was attempted programmatically but appears to have FAILED (${loginMessage}).
|
||||
@@ -767,7 +664,6 @@ Once you've verified the login state, proceed with the following task: ${task}
|
||||
${actionDirectives.length > 0 ? `\n\nNote on Secure Actions: You might see [SECURE ACTION X] placeholders. Handle these by outputting "EXECUTE SECURE ACTION X" when appropriate.` : ''}
|
||||
${outputSchema && typeof outputSchema === 'object' && outputSchema !== null ? `\n\nIMPORTANT: You MUST return your final result in the following JSON format exactly:\n${formatSchemaForInstructions(getSchemaObject(outputSchema))}\n\nYour response should consist of valid JSON only, with no additional text.` : ''}`
|
||||
} else {
|
||||
// Original detailed instructions if agent needs to handle login/placeholders
|
||||
agentInstructions = `You are a helpful web browsing assistant that will complete tasks on websites. Your goal is to accomplish the following task: ${processedTask}\n
|
||||
${actionDirectives.length > 0 ? `\n\nYou'll see [SECURE ACTION X] placeholders in the task. These represent secure actions that will be handled automatically when you navigate to the appropriate page. When you reach a point where a secure action should be performed, output a line with exactly: "EXECUTE SECURE ACTION X" (where X is the action number). Then wait for confirmation before proceeding.` : ''}\n
|
||||
IMPORTANT: For any form fields that require sensitive information like usernames or passwords:
|
||||
@@ -790,40 +686,37 @@ WEBSITE NAVIGATION GUIDANCE:
|
||||
${outputSchema && typeof outputSchema === 'object' && outputSchema !== null ? `\n\nIMPORTANT: You MUST return your final result in the following JSON format exactly:\n${formatSchemaForInstructions(getSchemaObject(outputSchema))}\n\nYour response should consist of valid JSON only, with no additional text. Ensure the data in your response adheres strictly to the schema provided.` : ''}`
|
||||
}
|
||||
|
||||
// Create agent to execute the task
|
||||
logger.info('Creating Stagehand agent', {
|
||||
directLoginAttempted,
|
||||
directLoginSuccess,
|
||||
loginMessage,
|
||||
})
|
||||
const agent = stagehand.agent({
|
||||
provider: 'anthropic',
|
||||
model: 'claude-3-7-sonnet-20250219',
|
||||
instructions: agentInstructions, // Use the generated instructions
|
||||
options: {
|
||||
apiKey: apiKey,
|
||||
// Conditional additional instructions based on direct login attempt
|
||||
additionalInstructions: directLoginAttempted
|
||||
? `Login was ${directLoginSuccess ? 'successfully completed' : 'attempted but failed'}.
|
||||
${loginMessage}
|
||||
First check the current state of the page.
|
||||
If login failed, you may need to click the login button again after ensuring fields are properly filled.`
|
||||
: `
|
||||
|
||||
const additionalContext = directLoginAttempted
|
||||
? `Login was ${directLoginSuccess ? 'successfully completed' : 'attempted but failed'}.
|
||||
${loginMessage}
|
||||
First check the current state of the page.
|
||||
If login failed, you may need to click the login button again after ensuring fields are properly filled.`
|
||||
: `
|
||||
This task may contain placeholder variables like %username% and %password%.
|
||||
When you need to fill form fields, use these placeholders directly (e.g., type "%username%").
|
||||
The system will substitute actual values when these placeholders are used, keeping sensitive data secure.
|
||||
`.trim(),
|
||||
`.trim()
|
||||
|
||||
const agent = stagehand.agent({
|
||||
model: {
|
||||
modelName: 'anthropic/claude-3-7-sonnet-latest',
|
||||
apiKey: apiKey,
|
||||
},
|
||||
executionModel: {
|
||||
modelName: 'anthropic/claude-3-7-sonnet-latest',
|
||||
apiKey: apiKey,
|
||||
},
|
||||
systemPrompt: `${agentInstructions}\n\n${additionalContext}`,
|
||||
})
|
||||
|
||||
// Since we can't use events directly, we'll need to handle secure actions
|
||||
// by running the agent and then processing any EXECUTE SECURE ACTION directives
|
||||
// in its output, then decide if we need to continue the conversation
|
||||
|
||||
// Sequence to execute agent with secure action processing
|
||||
const runAgentWithSecureActions = async (): Promise<any> => {
|
||||
// Use taskForAgent which might have been modified if direct login occurred
|
||||
let currentResult = await agent.execute(taskForAgent)
|
||||
let currentResult = await agent.execute({ instruction: taskForAgent })
|
||||
let allExecutedActions: Array<{
|
||||
action: string
|
||||
result: { success: boolean; message: string }
|
||||
@@ -833,17 +726,13 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
|
||||
while (iterationCount < maxIterations && stagehand !== null) {
|
||||
if (!currentResult.message) {
|
||||
// No message to process, we're done
|
||||
break
|
||||
}
|
||||
|
||||
// Check if there are secure action directives in the message
|
||||
if (!/EXECUTE SECURE ACTION \d+/i.test(currentResult.message)) {
|
||||
// No secure actions to execute, we're done
|
||||
break
|
||||
}
|
||||
|
||||
// Process secure actions in the message
|
||||
const { modifiedMessage, executedActions } = await processSecureActions(
|
||||
currentResult.message,
|
||||
stagehand,
|
||||
@@ -851,39 +740,28 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
variablesObject
|
||||
)
|
||||
|
||||
// Add executed actions to our collection
|
||||
allExecutedActions = [...allExecutedActions, ...executedActions]
|
||||
|
||||
if (executedActions.length === 0) {
|
||||
// No actions were executed, we can stop
|
||||
break
|
||||
}
|
||||
|
||||
// Continue conversation with the agent using the modified message as context
|
||||
iterationCount++
|
||||
|
||||
// Only continue if we need to - if we reached the final state
|
||||
// with structured output, don't keep going
|
||||
const hasStructuredOutput = /```json|^\s*{/.test(modifiedMessage)
|
||||
if (hasStructuredOutput) {
|
||||
// Already has structured output, let's not continue and risk losing it
|
||||
currentResult.message = modifiedMessage
|
||||
break
|
||||
}
|
||||
|
||||
// Continue the conversation with the agent
|
||||
logger.info(
|
||||
`Continuing agent execution with processed actions, iteration ${iterationCount}`
|
||||
)
|
||||
|
||||
try {
|
||||
// Here we'd continue the agent conversation, but since agent.execute
|
||||
// doesn't support continuation easily, we have to create a new prompt
|
||||
// that synthesizes what's happened so far
|
||||
const continuationPrompt = `${modifiedMessage}\n\nPlease continue with the task.`
|
||||
const nextResult = await agent.execute(continuationPrompt)
|
||||
const nextResult = await agent.execute({ instruction: continuationPrompt })
|
||||
|
||||
// Merge results - keep actions from both iterations but update message
|
||||
currentResult = {
|
||||
...nextResult,
|
||||
actions: [...currentResult.actions, ...nextResult.actions],
|
||||
@@ -894,16 +772,14 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
}
|
||||
}
|
||||
|
||||
// Return the final result and all executed secure actions
|
||||
return {
|
||||
...currentResult,
|
||||
secureActions: allExecutedActions,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the agent with secure action handling
|
||||
logger.info('Executing agent task', {
|
||||
task: taskForAgent, // Log the task actually given to the agent
|
||||
task: taskForAgent,
|
||||
actionDirectiveCount: actionDirectives.length,
|
||||
directLoginAttempted,
|
||||
directLoginSuccess,
|
||||
@@ -925,59 +801,51 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
executedActionCount: agentExecutionResult.secureActions?.length || 0,
|
||||
})
|
||||
|
||||
// Parse the structured data from the agent's message if possible
|
||||
let structuredOutput = null
|
||||
const hasOutputSchema =
|
||||
outputSchema && typeof outputSchema === 'object' && outputSchema !== null
|
||||
|
||||
if (agentResult.message) {
|
||||
try {
|
||||
// Try to parse JSON from the message
|
||||
// First, clean up the message to extract just the JSON
|
||||
let jsonContent = agentResult.message
|
||||
|
||||
// Look for JSON block markers in case the model wrapped it in ```json blocks
|
||||
const jsonBlockMatch = jsonContent.match(/```(?:json)?\s*([\s\S]*?)\s*```/)
|
||||
if (jsonBlockMatch?.[1]) {
|
||||
jsonContent = jsonBlockMatch[1]
|
||||
}
|
||||
|
||||
// Try to parse the content as JSON
|
||||
structuredOutput = JSON.parse(jsonContent)
|
||||
logger.info('Successfully parsed structured output from agent response')
|
||||
} catch (parseError) {
|
||||
logger.error('Failed to parse JSON from agent message', {
|
||||
error: parseError,
|
||||
message: agentResult.message,
|
||||
})
|
||||
if (hasOutputSchema) {
|
||||
logger.warn('Failed to parse JSON from agent message, attempting fallback extraction', {
|
||||
error: parseError,
|
||||
})
|
||||
|
||||
// If we have a schema, try one more approach with extract
|
||||
if (
|
||||
outputSchema &&
|
||||
typeof outputSchema === 'object' &&
|
||||
outputSchema !== null &&
|
||||
stagehand
|
||||
) {
|
||||
try {
|
||||
logger.info('Attempting to extract structured data using Stagehand extract')
|
||||
const schemaObj = getSchemaObject(outputSchema)
|
||||
const zodSchema = ensureZodObject(logger, schemaObj)
|
||||
if (stagehand) {
|
||||
try {
|
||||
logger.info('Attempting to extract structured data using Stagehand extract')
|
||||
const schemaObj = getSchemaObject(outputSchema)
|
||||
const zodSchema = ensureZodObject(logger, schemaObj)
|
||||
|
||||
// Use the extract API to get structured data from whatever page we ended up on
|
||||
structuredOutput = await stagehand.page.extract({
|
||||
instruction:
|
||||
structuredOutput = await stagehand.extract(
|
||||
'Extract the requested information from this page according to the schema',
|
||||
schema: zodSchema,
|
||||
} as any)
|
||||
zodSchema
|
||||
)
|
||||
|
||||
logger.info('Successfully extracted structured data as fallback', {
|
||||
keys: structuredOutput ? Object.keys(structuredOutput) : [],
|
||||
})
|
||||
} catch (extractError) {
|
||||
logger.error('Fallback extraction also failed', { error: extractError })
|
||||
logger.info('Successfully extracted structured data as fallback', {
|
||||
keys: structuredOutput ? Object.keys(structuredOutput) : [],
|
||||
})
|
||||
} catch (extractError) {
|
||||
logger.error('Fallback extraction also failed', { error: extractError })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.info('Agent returned plain text response (no schema provided)')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return agent result, structured output, and secure action results
|
||||
return NextResponse.json({
|
||||
agentResult,
|
||||
structuredOutput,
|
||||
@@ -990,7 +858,6 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
})
|
||||
|
||||
// Provide detailed error information
|
||||
let errorMessage = 'Unknown error during agent execution'
|
||||
let errorDetails: Record<string, any> = {}
|
||||
|
||||
@@ -1001,7 +868,6 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
stack: error.stack,
|
||||
}
|
||||
|
||||
// Log additional properties for context
|
||||
const errorObj = error as any
|
||||
if (typeof errorObj.code !== 'undefined') {
|
||||
errorDetails.code = errorObj.code
|
||||
@@ -1036,7 +902,6 @@ The system will substitute actual values when these placeholders are used, keepi
|
||||
{ status: 500 }
|
||||
)
|
||||
} finally {
|
||||
// Clean up Stagehand resources
|
||||
if (stagehand) {
|
||||
try {
|
||||
logger.info('Closing Stagehand instance')
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils'
|
||||
|
||||
const logger = createLogger('StagehandExtractAPI')
|
||||
|
||||
// Environment variables for Browserbase
|
||||
const BROWSERBASE_API_KEY = env.BROWSERBASE_API_KEY
|
||||
const BROWSERBASE_PROJECT_ID = env.BROWSERBASE_PROJECT_ID
|
||||
|
||||
@@ -21,7 +20,7 @@ const requestSchema = z.object({
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
let stagehand = null
|
||||
let stagehand: Stagehand | null = null
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
@@ -42,14 +41,13 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
const params = validationResult.data
|
||||
const { url: rawUrl, instruction, selector, useTextExtract, apiKey, schema } = params
|
||||
const { url: rawUrl, instruction, selector, apiKey, schema } = params
|
||||
const url = normalizeUrl(rawUrl)
|
||||
|
||||
logger.info('Starting Stagehand extraction process', {
|
||||
rawUrl,
|
||||
url,
|
||||
hasInstruction: !!instruction,
|
||||
useTextExtract: !!useTextExtract,
|
||||
schemaType: typeof schema,
|
||||
})
|
||||
|
||||
@@ -79,16 +77,16 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('Initializing Stagehand with Browserbase')
|
||||
logger.info('Initializing Stagehand with Browserbase (v3)')
|
||||
|
||||
stagehand = new Stagehand({
|
||||
env: 'BROWSERBASE',
|
||||
apiKey: BROWSERBASE_API_KEY,
|
||||
projectId: BROWSERBASE_PROJECT_ID,
|
||||
verbose: 1,
|
||||
logger: (msg) => logger.info(typeof msg === 'string' ? msg : JSON.stringify(msg)),
|
||||
disablePino: true,
|
||||
modelName: 'gpt-4o',
|
||||
modelClientOptions: {
|
||||
model: {
|
||||
modelName: 'openai/gpt-4o',
|
||||
apiKey: apiKey,
|
||||
},
|
||||
})
|
||||
@@ -97,8 +95,10 @@ export async function POST(request: NextRequest) {
|
||||
await stagehand.init()
|
||||
logger.info('Stagehand initialized successfully')
|
||||
|
||||
const page = stagehand.context.pages()[0]
|
||||
|
||||
logger.info(`Navigating to ${url}`)
|
||||
await stagehand.page.goto(url, { waitUntil: 'networkidle' })
|
||||
await page.goto(url, { waitUntil: 'networkidle' })
|
||||
logger.info('Navigation complete')
|
||||
|
||||
logger.info('Preparing extraction schema', {
|
||||
@@ -130,32 +130,19 @@ export async function POST(request: NextRequest) {
|
||||
zodSchema = undefined
|
||||
}
|
||||
|
||||
const extractOptions: any = {
|
||||
instruction,
|
||||
useTextExtract: !!useTextExtract,
|
||||
}
|
||||
|
||||
if (zodSchema) {
|
||||
extractOptions.schema = zodSchema
|
||||
}
|
||||
|
||||
if (selector) {
|
||||
logger.info(`Using selector: ${selector}`)
|
||||
extractOptions.selector = selector
|
||||
}
|
||||
|
||||
logger.info('Calling stagehand.page.extract with options', {
|
||||
hasInstruction: !!extractOptions.instruction,
|
||||
hasSchema: !!extractOptions.schema,
|
||||
hasSelector: !!extractOptions.selector,
|
||||
useTextExtract: extractOptions.useTextExtract,
|
||||
logger.info('Calling stagehand.extract with options', {
|
||||
hasInstruction: !!instruction,
|
||||
hasSchema: !!zodSchema,
|
||||
hasSelector: !!selector,
|
||||
})
|
||||
|
||||
let extractedData
|
||||
if (zodSchema) {
|
||||
extractedData = await stagehand.page.extract(extractOptions)
|
||||
extractedData = await stagehand.extract(instruction, zodSchema, {
|
||||
selector: selector || undefined,
|
||||
})
|
||||
} else {
|
||||
extractedData = await stagehand.page.extract(extractOptions.instruction)
|
||||
extractedData = await stagehand.extract(instruction)
|
||||
}
|
||||
|
||||
logger.info('Extraction successful', {
|
||||
|
||||
224
apps/sim/app/api/tools/wordpress/upload/route.ts
Normal file
224
apps/sim/app/api/tools/wordpress/upload/route.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
getFileExtension,
|
||||
getMimeTypeFromExtension,
|
||||
processSingleFileToUserFile,
|
||||
} from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('WordPressUploadAPI')
|
||||
|
||||
const WORDPRESS_COM_API_BASE = 'https://public-api.wordpress.com/wp/v2/sites'
|
||||
|
||||
const WordPressUploadSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
siteId: z.string().min(1, 'Site ID is required'),
|
||||
file: z.any().optional().nullable(),
|
||||
filename: z.string().optional().nullable(),
|
||||
title: z.string().optional().nullable(),
|
||||
caption: z.string().optional().nullable(),
|
||||
altText: z.string().optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized WordPress upload attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: authResult.error || 'Authentication required',
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Authenticated WordPress upload request via ${authResult.authType}`,
|
||||
{
|
||||
userId: authResult.userId,
|
||||
}
|
||||
)
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = WordPressUploadSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Uploading file to WordPress`, {
|
||||
siteId: validatedData.siteId,
|
||||
filename: validatedData.filename,
|
||||
hasFile: !!validatedData.file,
|
||||
})
|
||||
|
||||
if (!validatedData.file) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'No file provided. Please upload a file.',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Process file - convert to UserFile format if needed
|
||||
const fileData = validatedData.file
|
||||
|
||||
let userFile
|
||||
try {
|
||||
userFile = processSingleFileToUserFile(fileData, requestId, logger)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to process file',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Downloading file from storage`, {
|
||||
fileName: userFile.name,
|
||||
key: userFile.key,
|
||||
size: userFile.size,
|
||||
})
|
||||
|
||||
let fileBuffer: Buffer
|
||||
|
||||
try {
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Failed to download file:`, error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// Use provided filename or fall back to the original file name
|
||||
const filename = validatedData.filename || userFile.name
|
||||
const mimeType = userFile.type || getMimeTypeFromExtension(getFileExtension(filename))
|
||||
|
||||
logger.info(`[${requestId}] Uploading to WordPress`, {
|
||||
siteId: validatedData.siteId,
|
||||
filename,
|
||||
mimeType,
|
||||
size: fileBuffer.length,
|
||||
})
|
||||
|
||||
// Upload to WordPress using multipart form data
|
||||
const formData = new FormData()
|
||||
// Convert Buffer to Uint8Array for Blob compatibility
|
||||
const uint8Array = new Uint8Array(fileBuffer)
|
||||
const blob = new Blob([uint8Array], { type: mimeType })
|
||||
formData.append('file', blob, filename)
|
||||
|
||||
// Add optional metadata
|
||||
if (validatedData.title) {
|
||||
formData.append('title', validatedData.title)
|
||||
}
|
||||
if (validatedData.caption) {
|
||||
formData.append('caption', validatedData.caption)
|
||||
}
|
||||
if (validatedData.altText) {
|
||||
formData.append('alt_text', validatedData.altText)
|
||||
}
|
||||
if (validatedData.description) {
|
||||
formData.append('description', validatedData.description)
|
||||
}
|
||||
|
||||
const uploadResponse = await fetch(`${WORDPRESS_COM_API_BASE}/${validatedData.siteId}/media`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
const errorText = await uploadResponse.text()
|
||||
let errorMessage = `WordPress API error: ${uploadResponse.statusText}`
|
||||
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.message || errorJson.error || errorMessage
|
||||
} catch {
|
||||
// Use default error message
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] WordPress API error:`, {
|
||||
status: uploadResponse.status,
|
||||
statusText: uploadResponse.statusText,
|
||||
error: errorText,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
},
|
||||
{ status: uploadResponse.status }
|
||||
)
|
||||
}
|
||||
|
||||
const uploadData = await uploadResponse.json()
|
||||
|
||||
logger.info(`[${requestId}] File uploaded successfully`, {
|
||||
mediaId: uploadData.id,
|
||||
sourceUrl: uploadData.source_url,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
media: {
|
||||
id: uploadData.id,
|
||||
date: uploadData.date,
|
||||
slug: uploadData.slug,
|
||||
type: uploadData.type,
|
||||
link: uploadData.link,
|
||||
title: uploadData.title,
|
||||
caption: uploadData.caption,
|
||||
alt_text: uploadData.alt_text,
|
||||
media_type: uploadData.media_type,
|
||||
mime_type: uploadData.mime_type,
|
||||
source_url: uploadData.source_url,
|
||||
media_details: uploadData.media_details,
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Invalid request data',
|
||||
details: error.errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] Error uploading file to WordPress:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Internal server error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ export async function GET(request: NextRequest) {
|
||||
id: workflowExecutionLogs.id,
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
|
||||
level: workflowExecutionLogs.level,
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
@@ -161,6 +162,7 @@ export async function GET(request: NextRequest) {
|
||||
id: log.id,
|
||||
workflowId: log.workflowId,
|
||||
executionId: log.executionId,
|
||||
deploymentVersionId: log.deploymentVersionId,
|
||||
level: log.level,
|
||||
trigger: log.trigger,
|
||||
startedAt: log.startedAt.toISOString(),
|
||||
|
||||
@@ -430,6 +430,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
edges: any[]
|
||||
loops: Record<string, any>
|
||||
parallels: Record<string, any>
|
||||
deploymentVersionId?: string
|
||||
} | null = null
|
||||
|
||||
let processedInput = input
|
||||
@@ -444,6 +445,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
edges: workflowData.edges,
|
||||
loops: workflowData.loops || {},
|
||||
parallels: workflowData.parallels || {},
|
||||
deploymentVersionId:
|
||||
!shouldUseDraftState && 'deploymentVersionId' in workflowData
|
||||
? (workflowData.deploymentVersionId as string)
|
||||
: undefined,
|
||||
}
|
||||
|
||||
const serializedWorkflow = new Serializer().serializeWorkflow(
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { VerifiedBadge } from '@/components/ui/verified-badge'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
@@ -41,6 +42,95 @@ import { useStarTemplate, useTemplate } from '@/hooks/queries/templates'
|
||||
|
||||
const logger = createLogger('TemplateDetails')
|
||||
|
||||
interface TemplateDetailsLoadingProps {
|
||||
isWorkspaceContext?: boolean
|
||||
workspaceId?: string | null
|
||||
}
|
||||
|
||||
function TemplateDetailsLoading({ isWorkspaceContext, workspaceId }: TemplateDetailsLoadingProps) {
|
||||
const breadcrumbItems = [
|
||||
{
|
||||
label: 'Templates',
|
||||
href:
|
||||
isWorkspaceContext && workspaceId ? `/workspace/${workspaceId}/templates` : '/templates',
|
||||
},
|
||||
{ label: 'Template' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isWorkspaceContext ? 'h-full flex-1 overflow-hidden' : 'min-h-screen'
|
||||
)}
|
||||
>
|
||||
<div className={cn('flex flex-1', isWorkspaceContext && 'overflow-hidden')}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col px-[24px] pt-[24px] pb-[24px]',
|
||||
isWorkspaceContext ? 'overflow-auto' : 'overflow-visible'
|
||||
)}
|
||||
>
|
||||
{/* Breadcrumb navigation */}
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
|
||||
{/* Template name and action buttons */}
|
||||
<div className='mt-[14px] flex items-center justify-between'>
|
||||
<Skeleton className='h-[27px] w-[250px] rounded-[4px]' />
|
||||
<div className='flex items-center gap-[8px]'>
|
||||
<Skeleton className='h-[32px] w-[80px] rounded-[6px]' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Template tagline */}
|
||||
<div className='mt-[4px]'>
|
||||
<Skeleton className='h-[21px] w-[400px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Creator and stats row */}
|
||||
<div className='mt-[16px] flex items-center gap-[8px]'>
|
||||
{/* Star icon and count */}
|
||||
<Skeleton className='h-[14px] w-[14px] rounded-[2px]' />
|
||||
<Skeleton className='h-[21px] w-[24px] rounded-[4px]' />
|
||||
|
||||
{/* Views icon and count */}
|
||||
<Skeleton className='h-[16px] w-[16px] rounded-[2px]' />
|
||||
<Skeleton className='h-[21px] w-[32px] rounded-[4px]' />
|
||||
|
||||
{/* Vertical divider */}
|
||||
<div className='mx-[4px] mb-[-1.5px] h-[18px] w-[1.25px] rounded-full bg-[var(--border)]' />
|
||||
|
||||
{/* Creator profile pic */}
|
||||
<Skeleton className='h-[16px] w-[16px] rounded-full' />
|
||||
{/* Creator name */}
|
||||
<Skeleton className='h-[21px] w-[100px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Credentials needed */}
|
||||
<div className='mt-[12px]'>
|
||||
<Skeleton className='h-[18px] w-[280px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Canvas preview */}
|
||||
<div className='relative mt-[24px] h-[450px] w-full flex-shrink-0 overflow-hidden rounded-[8px] border border-[var(--border)]'>
|
||||
<Skeleton className='h-full w-full rounded-none' />
|
||||
</div>
|
||||
|
||||
{/* About this Workflow */}
|
||||
<div className='mt-8'>
|
||||
<Skeleton className='mb-4 h-[24px] w-[180px] rounded-[4px]' />
|
||||
<div className='space-y-2'>
|
||||
<Skeleton className='h-[18px] w-full rounded-[4px]' />
|
||||
<Skeleton className='h-[18px] w-[90%] rounded-[4px]' />
|
||||
<Skeleton className='h-[18px] w-[75%] rounded-[4px]' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface TemplateDetailsProps {
|
||||
isWorkspaceContext?: boolean
|
||||
}
|
||||
@@ -207,11 +297,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className='flex h-screen items-center justify-center'>
|
||||
<div className='text-center'>
|
||||
<p className='font-sans text-muted-foreground text-sm'>Loading template...</p>
|
||||
</div>
|
||||
</div>
|
||||
<TemplateDetailsLoading isWorkspaceContext={isWorkspaceContext} workspaceId={workspaceId} />
|
||||
)
|
||||
}
|
||||
|
||||
@@ -542,9 +628,19 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col', isWorkspaceContext ? 'h-full flex-1' : 'min-h-screen')}>
|
||||
<div className='flex flex-1 overflow-hidden'>
|
||||
<div className='flex flex-1 flex-col overflow-auto px-[24px] pt-[24px] pb-[24px]'>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isWorkspaceContext ? 'h-full flex-1 overflow-hidden' : 'min-h-screen'
|
||||
)}
|
||||
>
|
||||
<div className={cn('flex flex-1', isWorkspaceContext && 'overflow-hidden')}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col px-[24px] pt-[24px] pb-[24px]',
|
||||
isWorkspaceContext ? 'overflow-auto' : 'overflow-visible'
|
||||
)}
|
||||
>
|
||||
{/* Breadcrumb navigation */}
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
|
||||
@@ -697,7 +793,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
{/* Template tagline */}
|
||||
{template.details?.tagline && (
|
||||
<p className='mt-[4px] font-medium text-[14px] text-[var(--text-tertiary)]'>
|
||||
<p className='mt-[4px] line-clamp-2 max-w-[40vw] font-medium text-[14px] text-[var(--text-tertiary)]'>
|
||||
{template.details.tagline}
|
||||
</p>
|
||||
)}
|
||||
@@ -770,7 +866,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
{/* Canvas preview */}
|
||||
<div
|
||||
className='relative mt-[24px] h-[450px] w-full overflow-hidden rounded-[8px] border border-[var(--border)]'
|
||||
className='relative mt-[24px] h-[450px] w-full flex-shrink-0 overflow-hidden rounded-[8px] border border-[var(--border)]'
|
||||
onWheelCapture={handleCanvasWheelCapture}
|
||||
>
|
||||
{renderWorkflowPreview()}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { season } from '@/app/_styles/fonts/season/season'
|
||||
export default function TemplatesLayoutClient({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Tooltip.Provider delayDuration={600} skipDelayDuration={0}>
|
||||
<div className={`${season.variable} font-season`}>{children}</div>
|
||||
<div className={`${season.variable} flex min-h-screen flex-col font-season`}>{children}</div>
|
||||
</Tooltip.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from '@/components/emcn'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
|
||||
@@ -53,7 +52,7 @@ export function AddDocumentsModal({
|
||||
const [dragCounter, setDragCounter] = useState(0)
|
||||
const [retryingIndexes, setRetryingIndexes] = useState<Set<number>>(new Set())
|
||||
|
||||
const { isUploading, uploadProgress, uploadFiles, clearError } = useKnowledgeUpload({
|
||||
const { isUploading, uploadProgress, uploadFiles, uploadError, clearError } = useKnowledgeUpload({
|
||||
workspaceId,
|
||||
onUploadComplete: () => {
|
||||
logger.info(`Successfully uploaded ${files.length} files`)
|
||||
@@ -234,11 +233,7 @@ export function AddDocumentsModal({
|
||||
<div className='min-h-0 flex-1 overflow-y-auto'>
|
||||
<div className='space-y-[12px]'>
|
||||
{fileError && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{fileError}</AlertDescription>
|
||||
</Alert>
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>{fileError}</p>
|
||||
)}
|
||||
|
||||
<div className='flex flex-col gap-[8px]'>
|
||||
@@ -341,24 +336,31 @@ export function AddDocumentsModal({
|
||||
</div>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant='default' onClick={handleClose} type='button' disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
type='button'
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || isUploading}
|
||||
>
|
||||
{isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Uploading...'
|
||||
: 'Upload'}
|
||||
</Button>
|
||||
<ModalFooter className='flex-col items-stretch gap-[12px]'>
|
||||
{uploadError && (
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>
|
||||
{uploadError.message}
|
||||
</p>
|
||||
)}
|
||||
<div className='flex justify-end gap-[8px]'>
|
||||
<Button variant='default' onClick={handleClose} type='button' disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
type='button'
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || isUploading}
|
||||
>
|
||||
{isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Uploading...'
|
||||
: 'Upload'}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
ModalHeader,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
|
||||
@@ -89,7 +88,7 @@ export function CreateBaseModal({
|
||||
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { uploadFiles, isUploading, uploadProgress, clearError } = useKnowledgeUpload({
|
||||
const { uploadFiles, isUploading, uploadProgress, uploadError, clearError } = useKnowledgeUpload({
|
||||
workspaceId,
|
||||
onUploadComplete: (uploadedFiles) => {
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
@@ -280,21 +279,35 @@ export function CreateBaseModal({
|
||||
const newKnowledgeBase = result.data
|
||||
|
||||
if (files.length > 0) {
|
||||
newKnowledgeBase.docCount = files.length
|
||||
try {
|
||||
const uploadedFiles = await uploadFiles(files, newKnowledgeBase.id, {
|
||||
chunkSize: data.maxChunkSize,
|
||||
minCharactersPerChunk: data.minChunkSize,
|
||||
chunkOverlap: data.overlapSize,
|
||||
recipe: 'default',
|
||||
})
|
||||
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
logger.info(`Started processing ${uploadedFiles.length} documents in the background`)
|
||||
|
||||
newKnowledgeBase.docCount = uploadedFiles.length
|
||||
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
}
|
||||
} catch (uploadError) {
|
||||
// If file upload fails completely, delete the knowledge base to avoid orphaned empty KB
|
||||
logger.error('File upload failed, deleting knowledge base:', uploadError)
|
||||
try {
|
||||
await fetch(`/api/knowledge/${newKnowledgeBase.id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
logger.info(`Deleted orphaned knowledge base: ${newKnowledgeBase.id}`)
|
||||
} catch (deleteError) {
|
||||
logger.error('Failed to delete orphaned knowledge base:', deleteError)
|
||||
}
|
||||
throw uploadError
|
||||
}
|
||||
|
||||
const uploadedFiles = await uploadFiles(files, newKnowledgeBase.id, {
|
||||
chunkSize: data.maxChunkSize,
|
||||
minCharactersPerChunk: data.minChunkSize,
|
||||
chunkOverlap: data.overlapSize,
|
||||
recipe: 'default',
|
||||
})
|
||||
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
logger.info(`Started processing ${uploadedFiles.length} documents in the background`)
|
||||
} else {
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
@@ -325,14 +338,6 @@ export function CreateBaseModal({
|
||||
<ModalBody className='!pb-[16px]'>
|
||||
<div ref={scrollContainerRef} className='min-h-0 flex-1 overflow-y-auto'>
|
||||
<div className='space-y-[12px]'>
|
||||
{submitStatus && submitStatus.type === 'error' && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{submitStatus.message}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className='flex flex-col gap-[8px]'>
|
||||
<Label htmlFor='name'>Name</Label>
|
||||
<Input
|
||||
@@ -498,36 +503,39 @@ export function CreateBaseModal({
|
||||
)}
|
||||
|
||||
{fileError && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{fileError}</AlertDescription>
|
||||
</Alert>
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>{fileError}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={() => handleClose(false)}
|
||||
type='button'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='primary' type='submit' disabled={isSubmitting || !nameValue?.trim()}>
|
||||
{isSubmitting
|
||||
? isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Creating...'
|
||||
: 'Creating...'
|
||||
: 'Create'}
|
||||
</Button>
|
||||
<ModalFooter className='flex-col items-stretch gap-[12px]'>
|
||||
{(submitStatus?.type === 'error' || uploadError) && (
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>
|
||||
{uploadError?.message || submitStatus?.message}
|
||||
</p>
|
||||
)}
|
||||
<div className='flex justify-end gap-[8px]'>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={() => handleClose(false)}
|
||||
type='button'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='primary' type='submit' disabled={isSubmitting || !nameValue?.trim()}>
|
||||
{isSubmitting
|
||||
? isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Creating...'
|
||||
: 'Creating...'
|
||||
: 'Create'}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalContent>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils'
|
||||
|
||||
const logger = createLogger('KnowledgeUpload')
|
||||
|
||||
@@ -143,6 +144,17 @@ const calculateThroughputMbps = (bytes: number, durationMs: number) => {
|
||||
*/
|
||||
const formatDurationSeconds = (durationMs: number) => Number((durationMs / 1000).toFixed(2))
|
||||
|
||||
/**
|
||||
* Gets the content type for a file, falling back to extension-based lookup if browser doesn't provide one
|
||||
*/
|
||||
const getFileContentType = (file: File): string => {
|
||||
if (file.type?.trim()) {
|
||||
return file.type
|
||||
}
|
||||
const extension = getFileExtension(file.name)
|
||||
return getMimeTypeFromExtension(extension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs async operations with concurrency limit
|
||||
*/
|
||||
@@ -280,7 +292,7 @@ const getPresignedData = async (
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
}),
|
||||
signal: localController.signal,
|
||||
@@ -529,7 +541,9 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
throughputMbps: calculateThroughputMbps(file.size, durationMs),
|
||||
status: xhr.status,
|
||||
})
|
||||
resolve(createUploadedFile(file.name, fullFileUrl, file.size, file.type, file))
|
||||
resolve(
|
||||
createUploadedFile(file.name, fullFileUrl, file.size, getFileContentType(file), file)
|
||||
)
|
||||
} else {
|
||||
logger.error('S3 PUT request failed', {
|
||||
status: xhr.status,
|
||||
@@ -597,7 +611,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
}),
|
||||
})
|
||||
@@ -736,7 +750,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
|
||||
const fullFileUrl = path.startsWith('http') ? path : `${window.location.origin}${path}`
|
||||
|
||||
return createUploadedFile(file.name, fullFileUrl, file.size, file.type, file)
|
||||
return createUploadedFile(file.name, fullFileUrl, file.size, getFileContentType(file), file)
|
||||
} catch (error) {
|
||||
logger.error(`Multipart upload failed for ${file.name}:`, error)
|
||||
const durationMs = getHighResTime() - startTime
|
||||
@@ -800,7 +814,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
file.name,
|
||||
filePath.startsWith('http') ? filePath : `${window.location.origin}${filePath}`,
|
||||
file.size,
|
||||
file.type,
|
||||
getFileContentType(file),
|
||||
file
|
||||
)
|
||||
} finally {
|
||||
@@ -855,7 +869,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
const batchRequest = {
|
||||
files: batchFiles.map((file) => ({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
})),
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ export function Knowledge() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-[24px] grid grid-cols-1 gap-x-[20px] gap-y-[40px] md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
|
||||
<div className='mt-[24px] grid grid-cols-1 gap-[20px] md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
|
||||
{isLoading ? (
|
||||
<BaseCardSkeletonGrid count={8} />
|
||||
) : filteredAndSortedKnowledgeBases.length === 0 ? (
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
* Knowledge Base layout - applies sidebar padding for all knowledge routes.
|
||||
*/
|
||||
export default function KnowledgeLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className='flex h-full flex-1 flex-col pl-60'>{children}</div>
|
||||
return <div className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</div>
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ export function LineChart({
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
const uniqueId = useRef(`chart-${Math.random().toString(36).substring(2, 9)}`).current
|
||||
const [containerWidth, setContainerWidth] = useState<number>(420)
|
||||
const width = containerWidth
|
||||
const [containerWidth, setContainerWidth] = useState<number | null>(null)
|
||||
const width = containerWidth ?? 0
|
||||
const height = 166
|
||||
const padding = { top: 16, right: 28, bottom: 26, left: 26 }
|
||||
useEffect(() => {
|
||||
@@ -39,14 +39,14 @@ export function LineChart({
|
||||
const element = containerRef.current
|
||||
const ro = new ResizeObserver((entries) => {
|
||||
const entry = entries[0]
|
||||
if (entry?.contentRect) {
|
||||
if (entry?.contentRect && entry.contentRect.width > 0) {
|
||||
const w = Math.max(280, Math.floor(entry.contentRect.width))
|
||||
setContainerWidth(w)
|
||||
}
|
||||
})
|
||||
ro.observe(element)
|
||||
const rect = element.getBoundingClientRect()
|
||||
if (rect?.width) setContainerWidth(Math.max(280, Math.floor(rect.width)))
|
||||
if (rect?.width && rect.width > 0) setContainerWidth(Math.max(280, Math.floor(rect.width)))
|
||||
return () => ro.disconnect()
|
||||
}, [])
|
||||
const chartWidth = width - padding.left - padding.right
|
||||
@@ -95,6 +95,16 @@ export function LineChart({
|
||||
|
||||
const hasExternalWrapper = !label || label === ''
|
||||
|
||||
if (containerWidth === null) {
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={cn('w-full', !hasExternalWrapper && 'rounded-lg border bg-card p-4')}
|
||||
style={{ height }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -40,6 +40,14 @@ interface WorkflowExecution {
|
||||
const DEFAULT_SEGMENTS = 72
|
||||
const MIN_SEGMENT_PX = 10
|
||||
|
||||
/**
|
||||
* Predetermined heights for skeleton bars to avoid hydration mismatch.
|
||||
* Using static values instead of Math.random() ensures server/client consistency.
|
||||
*/
|
||||
const SKELETON_BAR_HEIGHTS = [
|
||||
45, 72, 38, 85, 52, 68, 30, 90, 55, 42, 78, 35, 88, 48, 65, 28, 82, 58, 40, 75, 32, 95, 50, 70,
|
||||
]
|
||||
|
||||
/**
|
||||
* Skeleton loader for a single graph card
|
||||
*/
|
||||
@@ -56,12 +64,12 @@ function GraphCardSkeleton({ title }: { title: string }) {
|
||||
<div className='flex h-[166px] flex-col justify-end gap-[4px]'>
|
||||
{/* Skeleton bars simulating chart */}
|
||||
<div className='flex items-end gap-[2px]'>
|
||||
{Array.from({ length: 24 }).map((_, i) => (
|
||||
{SKELETON_BAR_HEIGHTS.map((height, i) => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
className='flex-1'
|
||||
style={{
|
||||
height: `${Math.random() * 80 + 20}%`,
|
||||
height: `${height}%`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
@@ -131,7 +139,7 @@ function WorkflowsListSkeleton({ rowCount = 5 }: { rowCount?: number }) {
|
||||
*/
|
||||
function DashboardSkeleton() {
|
||||
return (
|
||||
<div className='mt-[24px] flex min-h-0 flex-1 flex-col'>
|
||||
<div className='mt-[24px] flex min-h-0 flex-1 flex-col pb-[24px]'>
|
||||
{/* Graphs Section */}
|
||||
<div className='mb-[16px] flex-shrink-0'>
|
||||
<div className='grid grid-cols-1 gap-[16px] md:grid-cols-3'>
|
||||
@@ -289,9 +297,19 @@ export default function Dashboard({
|
||||
|
||||
const executions = metricsQuery.data?.workflows ?? []
|
||||
const aggregateSegments = metricsQuery.data?.aggregateSegments ?? []
|
||||
const loading = metricsQuery.isLoading
|
||||
const error = metricsQuery.error?.message ?? null
|
||||
|
||||
/**
|
||||
* Loading state logic using TanStack Query best practices:
|
||||
* - isPending: true when there's no cached data (initial load only)
|
||||
* - isFetching: true when any fetch is in progress
|
||||
* - isPlaceholderData: true when showing stale data from keepPreviousData
|
||||
*
|
||||
* We only show skeleton on initial load (isPending + no data).
|
||||
* For subsequent fetches, keepPreviousData shows stale content while fetching.
|
||||
*/
|
||||
const showSkeleton = metricsQuery.isPending && !metricsQuery.data
|
||||
|
||||
// Check if any filters are actually applied
|
||||
const hasActiveFilters = useMemo(
|
||||
() =>
|
||||
@@ -747,7 +765,7 @@ export default function Dashboard({
|
||||
}
|
||||
}, [refreshTrigger])
|
||||
|
||||
if (loading) {
|
||||
if (showSkeleton) {
|
||||
return <DashboardSkeleton />
|
||||
}
|
||||
|
||||
@@ -774,7 +792,7 @@ export default function Dashboard({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-[24px] flex min-h-0 flex-1 flex-col'>
|
||||
<div className='mt-[24px] flex min-h-0 flex-1 flex-col pb-[24px]'>
|
||||
{/* Graphs Section */}
|
||||
<div className='mb-[16px] flex-shrink-0'>
|
||||
<div className='grid grid-cols-1 gap-[16px] md:grid-cols-3'>
|
||||
@@ -793,7 +811,6 @@ export default function Dashboard({
|
||||
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
|
||||
{globalDetails ? (
|
||||
<LineChart
|
||||
key={`runs-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
|
||||
data={globalDetails.executionCounts}
|
||||
label=''
|
||||
color='var(--brand-tertiary)'
|
||||
@@ -822,7 +839,6 @@ export default function Dashboard({
|
||||
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
|
||||
{globalDetails ? (
|
||||
<LineChart
|
||||
key={`errors-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
|
||||
data={globalDetails.failureCounts}
|
||||
label=''
|
||||
color='var(--text-error)'
|
||||
@@ -851,7 +867,6 @@ export default function Dashboard({
|
||||
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
|
||||
{globalDetails ? (
|
||||
<LineChart
|
||||
key={`latency-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
|
||||
data={globalDetails.latencies}
|
||||
label=''
|
||||
color='var(--c-F59E0B)'
|
||||
|
||||
@@ -161,9 +161,9 @@ export function LogDetails({
|
||||
<ScrollArea className='mt-[20px] h-full w-full overflow-y-auto' ref={scrollAreaRef}>
|
||||
<div className='flex flex-col gap-[10px] pb-[16px]'>
|
||||
{/* Timestamp & Workflow Row */}
|
||||
<div className='flex items-center gap-[16px] px-[1px]'>
|
||||
<div className='flex min-w-0 items-center gap-[16px] px-[1px]'>
|
||||
{/* Timestamp Card */}
|
||||
<div className='flex w-[140px] flex-col gap-[8px]'>
|
||||
<div className='flex w-[140px] flex-shrink-0 flex-col gap-[8px]'>
|
||||
<div className='font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Timestamp
|
||||
</div>
|
||||
@@ -179,16 +179,16 @@ export function LogDetails({
|
||||
|
||||
{/* Workflow Card */}
|
||||
{log.workflow && (
|
||||
<div className='flex flex-col gap-[8px]'>
|
||||
<div className='flex w-0 min-w-0 flex-1 flex-col gap-[8px]'>
|
||||
<div className='font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Workflow
|
||||
</div>
|
||||
<div className='flex items-center gap-[8px]'>
|
||||
<div className='flex min-w-0 items-center gap-[8px]'>
|
||||
<div
|
||||
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
|
||||
style={{ backgroundColor: log.workflow?.color }}
|
||||
/>
|
||||
<span className='font-medium text-[14px] text-[var(--text-secondary)]'>
|
||||
<span className='min-w-0 flex-1 truncate font-medium text-[14px] text-[var(--text-secondary)]'>
|
||||
{log.workflow.name}
|
||||
</span>
|
||||
</div>
|
||||
@@ -209,7 +209,7 @@ export function LogDetails({
|
||||
)}
|
||||
|
||||
{/* Details Section */}
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex min-w-0 flex-col overflow-hidden'>
|
||||
{/* Level */}
|
||||
<div className='flex h-[48px] items-center justify-between border-[var(--border)] border-b p-[8px]'>
|
||||
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
@@ -233,14 +233,30 @@ export function LogDetails({
|
||||
</div>
|
||||
|
||||
{/* Duration */}
|
||||
<div className='flex h-[48px] items-center justify-between p-[8px]'>
|
||||
<div
|
||||
className={`flex h-[48px] items-center justify-between border-b p-[8px] ${log.deploymentVersion ? 'border-[var(--border)]' : 'border-transparent'}`}
|
||||
>
|
||||
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Duration
|
||||
</span>
|
||||
<span className='font-medium text-[14px] text-[var(--text-secondary)]'>
|
||||
<span className='font-medium text-[13px] text-[var(--text-secondary)]'>
|
||||
{log.duration || '—'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Version */}
|
||||
{log.deploymentVersion && (
|
||||
<div className='flex h-[48px] items-center gap-[8px] p-[8px]'>
|
||||
<span className='flex-shrink-0 font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Version
|
||||
</span>
|
||||
<div className='flex w-0 flex-1 justify-end'>
|
||||
<span className='max-w-full truncate rounded-[6px] bg-[#14291B] px-[9px] py-[2px] font-medium text-[#86EFAC] text-[12px]'>
|
||||
{log.deploymentVersionName || `v${log.deploymentVersion}`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Workflow State */}
|
||||
|
||||
@@ -343,17 +343,18 @@ export function LogsToolbar({
|
||||
</Button>
|
||||
|
||||
{/* View mode toggle */}
|
||||
<div className='flex h-[32px] items-center rounded-[6px] border border-[var(--border)] bg-[var(--surface-elevated)] p-[2px]'>
|
||||
<div
|
||||
className='flex h-[32px] cursor-pointer items-center rounded-[6px] border border-[var(--border)] bg-[var(--surface-elevated)] p-[2px]'
|
||||
onClick={() => onViewModeChange(isDashboardView ? 'logs' : 'dashboard')}
|
||||
>
|
||||
<Button
|
||||
variant={!isDashboardView ? 'active' : 'ghost'}
|
||||
onClick={() => onViewModeChange('logs')}
|
||||
className='h-[26px] rounded-[4px] px-[10px]'
|
||||
>
|
||||
Logs
|
||||
</Button>
|
||||
<Button
|
||||
variant={isDashboardView ? 'active' : 'ghost'}
|
||||
onClick={() => onViewModeChange('dashboard')}
|
||||
className='h-[26px] rounded-[4px] px-[10px]'
|
||||
>
|
||||
Dashboard
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
* Logs layout - applies sidebar padding for all logs routes.
|
||||
*/
|
||||
export default function LogsLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className='flex h-full flex-1 flex-col pl-60'>{children}</div>
|
||||
return <div className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</div>
|
||||
}
|
||||
|
||||
@@ -408,215 +408,216 @@ export default function Logs() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Dashboard view */}
|
||||
{isDashboardView && (
|
||||
<div className='pr-[24px] pb-[24px]'>
|
||||
<Dashboard isLive={isLive} refreshTrigger={dashboardRefreshTrigger} />
|
||||
</div>
|
||||
)}
|
||||
{/* Dashboard view - always mounted to preserve state and query cache */}
|
||||
<div
|
||||
className={cn('flex min-h-0 flex-1 flex-col pr-[24px]', !isDashboardView && 'hidden')}
|
||||
>
|
||||
<Dashboard isLive={isLive} refreshTrigger={dashboardRefreshTrigger} />
|
||||
</div>
|
||||
|
||||
{/* Main content area with table - only show in logs view */}
|
||||
{!isDashboardView && (
|
||||
<div className='relative mt-[24px] flex min-h-0 flex-1 overflow-hidden rounded-[6px]'>
|
||||
{/* Table container */}
|
||||
<div className='relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-[6px] bg-[var(--surface-1)]'>
|
||||
{/* Table header */}
|
||||
<div className='flex-shrink-0 rounded-t-[6px] bg-[var(--surface-3)] px-[24px] py-[10px]'>
|
||||
<div className='flex items-center'>
|
||||
<span className='w-[8%] min-w-[70px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Date
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Time
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[100px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Status
|
||||
</span>
|
||||
<span className='w-[22%] min-w-[140px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Workflow
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Cost
|
||||
</span>
|
||||
<span className='w-[14%] min-w-[110px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Trigger
|
||||
</span>
|
||||
<span className='w-[20%] min-w-[100px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Duration
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table body - scrollable */}
|
||||
<div
|
||||
className='min-h-0 flex-1 overflow-y-auto overflow-x-hidden'
|
||||
ref={scrollContainerRef}
|
||||
>
|
||||
{logsQuery.isLoading && !logsQuery.data ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-secondary)]'>
|
||||
<Loader2 className='h-[16px] w-[16px] animate-spin' />
|
||||
<span className='text-[13px]'>Loading logs...</span>
|
||||
</div>
|
||||
</div>
|
||||
) : logsQuery.isError ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-error)]'>
|
||||
<AlertCircle className='h-[16px] w-[16px]' />
|
||||
<span className='text-[13px]'>
|
||||
Error: {logsQuery.error?.message || 'Failed to load logs'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : logs.length === 0 ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-secondary)]'>
|
||||
<span className='text-[13px]'>No logs found</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{logs.map((log) => {
|
||||
const formattedDate = formatDate(log.createdAt)
|
||||
const isSelected = selectedLog?.id === log.id
|
||||
const baseLevel = (log.level || 'info').toLowerCase()
|
||||
const isError = baseLevel === 'error'
|
||||
const isPending = !isError && log.hasPendingPause === true
|
||||
const isRunning = !isError && !isPending && log.duration === null
|
||||
|
||||
return (
|
||||
<div
|
||||
key={log.id}
|
||||
ref={isSelected ? selectedRowRef : null}
|
||||
className={cn(
|
||||
'relative flex h-[44px] cursor-pointer items-center px-[24px] hover:bg-[var(--c-2A2A2A)]',
|
||||
isSelected && 'bg-[var(--c-2A2A2A)]'
|
||||
)}
|
||||
onClick={() => handleLogClick(log)}
|
||||
>
|
||||
<div className='flex flex-1 items-center'>
|
||||
{/* Date */}
|
||||
<span className='w-[8%] min-w-[70px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{formattedDate.compactDate}
|
||||
</span>
|
||||
|
||||
{/* Time */}
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{formattedDate.compactTime}
|
||||
</span>
|
||||
|
||||
{/* Status */}
|
||||
<div className='w-[12%] min-w-[100px]'>
|
||||
<StatusBadge
|
||||
status={
|
||||
isError
|
||||
? 'error'
|
||||
: isPending
|
||||
? 'pending'
|
||||
: isRunning
|
||||
? 'running'
|
||||
: 'info'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Workflow */}
|
||||
<div className='flex w-[22%] min-w-[140px] items-center gap-[8px] pr-[8px]'>
|
||||
<div
|
||||
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
|
||||
style={{ backgroundColor: log.workflow?.color }}
|
||||
/>
|
||||
<span className='min-w-0 truncate font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{log.workflow?.name || 'Unknown'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Cost */}
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{typeof log.cost?.total === 'number'
|
||||
? `$${log.cost.total.toFixed(4)}`
|
||||
: '—'}
|
||||
</span>
|
||||
|
||||
{/* Trigger */}
|
||||
<div className='w-[14%] min-w-[110px]'>
|
||||
{log.trigger ? (
|
||||
<TriggerBadge trigger={log.trigger} />
|
||||
) : (
|
||||
<span className='font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
—
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Duration */}
|
||||
<div className='w-[20%] min-w-[100px]'>
|
||||
<Badge
|
||||
variant='default'
|
||||
className='rounded-[6px] px-[9px] py-[2px] text-[12px]'
|
||||
>
|
||||
{formatDuration(log.duration) || '—'}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resume Link */}
|
||||
{isPending &&
|
||||
log.executionId &&
|
||||
(log.workflow?.id || log.workflowId) && (
|
||||
<Link
|
||||
href={`/resume/${log.workflow?.id || log.workflowId}/${log.executionId}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className={cn(
|
||||
buttonVariants({ variant: 'active' }),
|
||||
'absolute right-[24px] h-[26px] w-[26px] rounded-[6px] p-0'
|
||||
)}
|
||||
aria-label='Open resume console'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ArrowUpRight className='h-[14px] w-[14px]' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Infinite scroll loader */}
|
||||
{logsQuery.hasNextPage && (
|
||||
<div className='flex items-center justify-center py-[16px]'>
|
||||
<div
|
||||
ref={loaderRef}
|
||||
className='flex items-center gap-[8px] text-[var(--text-secondary)]'
|
||||
>
|
||||
{logsQuery.isFetchingNextPage ? (
|
||||
<>
|
||||
<Loader2 className='h-[16px] w-[16px] animate-spin' />
|
||||
<span className='text-[13px]'>Loading more...</span>
|
||||
</>
|
||||
) : (
|
||||
<span className='text-[13px]'>Scroll to load more</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
'relative mt-[24px] flex min-h-0 flex-1 flex-col overflow-hidden rounded-[6px]',
|
||||
isDashboardView && 'hidden'
|
||||
)}
|
||||
>
|
||||
{/* Table container */}
|
||||
<div className='relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-[6px] bg-[var(--surface-1)]'>
|
||||
{/* Table header */}
|
||||
<div className='flex-shrink-0 rounded-t-[6px] bg-[var(--surface-3)] px-[24px] py-[10px]'>
|
||||
<div className='flex items-center'>
|
||||
<span className='w-[8%] min-w-[70px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Date
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Time
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[100px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Status
|
||||
</span>
|
||||
<span className='w-[22%] min-w-[140px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Workflow
|
||||
</span>
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Cost
|
||||
</span>
|
||||
<span className='w-[14%] min-w-[110px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Trigger
|
||||
</span>
|
||||
<span className='w-[20%] min-w-[100px] font-medium text-[12px] text-[var(--text-tertiary)]'>
|
||||
Duration
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Log Details - rendered inside table container */}
|
||||
<LogDetails
|
||||
log={logDetailQuery.data || selectedLog}
|
||||
isOpen={isSidebarOpen}
|
||||
onClose={handleCloseSidebar}
|
||||
onNavigateNext={handleNavigateNext}
|
||||
onNavigatePrev={handleNavigatePrev}
|
||||
hasNext={selectedLogIndex < logs.length - 1}
|
||||
hasPrev={selectedLogIndex > 0}
|
||||
/>
|
||||
{/* Table body - scrollable */}
|
||||
<div
|
||||
className='min-h-0 flex-1 overflow-y-auto overflow-x-hidden'
|
||||
ref={scrollContainerRef}
|
||||
>
|
||||
{logsQuery.isLoading && !logsQuery.data ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-secondary)]'>
|
||||
<Loader2 className='h-[16px] w-[16px] animate-spin' />
|
||||
<span className='text-[13px]'>Loading logs...</span>
|
||||
</div>
|
||||
</div>
|
||||
) : logsQuery.isError ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-error)]'>
|
||||
<AlertCircle className='h-[16px] w-[16px]' />
|
||||
<span className='text-[13px]'>
|
||||
Error: {logsQuery.error?.message || 'Failed to load logs'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : logs.length === 0 ? (
|
||||
<div className='flex h-full items-center justify-center'>
|
||||
<div className='flex items-center gap-[8px] text-[var(--text-secondary)]'>
|
||||
<span className='text-[13px]'>No logs found</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{logs.map((log) => {
|
||||
const formattedDate = formatDate(log.createdAt)
|
||||
const isSelected = selectedLog?.id === log.id
|
||||
const baseLevel = (log.level || 'info').toLowerCase()
|
||||
const isError = baseLevel === 'error'
|
||||
const isPending = !isError && log.hasPendingPause === true
|
||||
const isRunning = !isError && !isPending && log.duration === null
|
||||
|
||||
return (
|
||||
<div
|
||||
key={log.id}
|
||||
ref={isSelected ? selectedRowRef : null}
|
||||
className={cn(
|
||||
'relative flex h-[44px] cursor-pointer items-center px-[24px] hover:bg-[var(--c-2A2A2A)]',
|
||||
isSelected && 'bg-[var(--c-2A2A2A)]'
|
||||
)}
|
||||
onClick={() => handleLogClick(log)}
|
||||
>
|
||||
<div className='flex flex-1 items-center'>
|
||||
{/* Date */}
|
||||
<span className='w-[8%] min-w-[70px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{formattedDate.compactDate}
|
||||
</span>
|
||||
|
||||
{/* Time */}
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{formattedDate.compactTime}
|
||||
</span>
|
||||
|
||||
{/* Status */}
|
||||
<div className='w-[12%] min-w-[100px]'>
|
||||
<StatusBadge
|
||||
status={
|
||||
isError
|
||||
? 'error'
|
||||
: isPending
|
||||
? 'pending'
|
||||
: isRunning
|
||||
? 'running'
|
||||
: 'info'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Workflow */}
|
||||
<div className='flex w-[22%] min-w-[140px] items-center gap-[8px] pr-[8px]'>
|
||||
<div
|
||||
className='h-[10px] w-[10px] flex-shrink-0 rounded-[3px]'
|
||||
style={{ backgroundColor: log.workflow?.color }}
|
||||
/>
|
||||
<span className='min-w-0 truncate font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{log.workflow?.name || 'Unknown'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Cost */}
|
||||
<span className='w-[12%] min-w-[90px] font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
{typeof log.cost?.total === 'number'
|
||||
? `$${log.cost.total.toFixed(4)}`
|
||||
: '—'}
|
||||
</span>
|
||||
|
||||
{/* Trigger */}
|
||||
<div className='w-[14%] min-w-[110px]'>
|
||||
{log.trigger ? (
|
||||
<TriggerBadge trigger={log.trigger} />
|
||||
) : (
|
||||
<span className='font-medium text-[12px] text-[var(--text-primary)]'>
|
||||
—
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Duration */}
|
||||
<div className='w-[20%] min-w-[100px]'>
|
||||
<Badge
|
||||
variant='default'
|
||||
className='rounded-[6px] px-[9px] py-[2px] text-[12px]'
|
||||
>
|
||||
{formatDuration(log.duration) || '—'}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resume Link */}
|
||||
{isPending && log.executionId && (log.workflow?.id || log.workflowId) && (
|
||||
<Link
|
||||
href={`/resume/${log.workflow?.id || log.workflowId}/${log.executionId}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className={cn(
|
||||
buttonVariants({ variant: 'active' }),
|
||||
'absolute right-[24px] h-[26px] w-[26px] rounded-[6px] p-0'
|
||||
)}
|
||||
aria-label='Open resume console'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<ArrowUpRight className='h-[14px] w-[14px]' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Infinite scroll loader */}
|
||||
{logsQuery.hasNextPage && (
|
||||
<div className='flex items-center justify-center py-[16px]'>
|
||||
<div
|
||||
ref={loaderRef}
|
||||
className='flex items-center gap-[8px] text-[var(--text-secondary)]'
|
||||
>
|
||||
{logsQuery.isFetchingNextPage ? (
|
||||
<>
|
||||
<Loader2 className='h-[16px] w-[16px] animate-spin' />
|
||||
<span className='text-[13px]'>Loading more...</span>
|
||||
</>
|
||||
) : (
|
||||
<span className='text-[13px]'>Scroll to load more</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Log Details - rendered inside table container */}
|
||||
<LogDetails
|
||||
log={logDetailQuery.data ? { ...selectedLog, ...logDetailQuery.data } : selectedLog}
|
||||
isOpen={isSidebarOpen}
|
||||
onClose={handleCloseSidebar}
|
||||
onNavigateNext={handleNavigateNext}
|
||||
onNavigatePrev={handleNavigatePrev}
|
||||
hasNext={selectedLogIndex < logs.length - 1}
|
||||
hasPrev={selectedLogIndex > 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -206,7 +206,10 @@ function TemplateCardInner({
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div ref={previewRef} className='relative h-[180px] w-full overflow-hidden rounded-[6px]'>
|
||||
<div
|
||||
ref={previewRef}
|
||||
className='pointer-events-none h-[180px] w-full overflow-hidden rounded-[6px]'
|
||||
>
|
||||
{normalizedState && isInView ? (
|
||||
<WorkflowPreview
|
||||
workflowState={normalizedState}
|
||||
@@ -222,8 +225,6 @@ function TemplateCardInner({
|
||||
) : (
|
||||
<div className='h-full w-full bg-[#2A2A2A]' />
|
||||
)}
|
||||
{/* Transparent overlay to block all pointer events from the preview */}
|
||||
<div className='pointer-events-none absolute inset-0' />
|
||||
</div>
|
||||
|
||||
<div className='mt-[10px] flex items-center justify-between'>
|
||||
|
||||
@@ -2,9 +2,5 @@
|
||||
* Templates layout - applies sidebar padding for all template routes.
|
||||
*/
|
||||
export default function TemplatesLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<main className='flex h-full flex-1 flex-col overflow-hidden pl-60'>
|
||||
<div>{children}</div>
|
||||
</main>
|
||||
)
|
||||
return <main className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</main>
|
||||
}
|
||||
|
||||
@@ -175,8 +175,8 @@ export default function Templates({
|
||||
<div className='flex flex-1 flex-col overflow-auto px-[24px] pt-[28px] pb-[24px]'>
|
||||
<div>
|
||||
<div className='flex items-start gap-[12px]'>
|
||||
<div className='flex h-[26px] w-[26px] items-center justify-center rounded-[6px] border border-[#1E3A5A] bg-[#0F2A3D]'>
|
||||
<Layout className='h-[14px] w-[14px] text-[#60A5FA]' />
|
||||
<div className='flex h-[26px] w-[26px] items-center justify-center rounded-[6px] border border-[#1A5070] bg-[#153347]'>
|
||||
<Layout className='h-[14px] w-[14px] text-[#33b4ff]' />
|
||||
</div>
|
||||
<h1 className='font-medium text-[18px]'>Templates</h1>
|
||||
</div>
|
||||
|
||||
@@ -398,8 +398,8 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(({ panelWidth }, ref
|
||||
className='flex h-full flex-col overflow-hidden'
|
||||
>
|
||||
{/* Header */}
|
||||
<div className='flex flex-shrink-0 items-center justify-between rounded-[4px] bg-[var(--surface-5)] px-[12px] py-[8px]'>
|
||||
<h2 className='font-medium text-[14px] text-[var(--text-primary)]'>
|
||||
<div className='flex flex-shrink-0 items-center justify-between gap-[8px] rounded-[4px] bg-[var(--surface-5)] px-[12px] py-[8px]'>
|
||||
<h2 className='min-w-0 flex-1 truncate font-medium text-[14px] text-[var(--text-primary)]'>
|
||||
{currentChat?.title || 'New Chat'}
|
||||
</h2>
|
||||
<div className='flex items-center gap-[8px]'>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton, TagInput } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
|
||||
import {
|
||||
@@ -87,7 +88,8 @@ export function TemplateDeploy({
|
||||
const deleteMutation = useDeleteTemplate()
|
||||
|
||||
const isSubmitting = createMutation.isPending || updateMutation.isPending
|
||||
const isFormValid = formData.name.trim().length > 0 && formData.name.length <= 100
|
||||
const isFormValid =
|
||||
formData.name.trim().length > 0 && formData.name.length <= 100 && formData.tagline.length <= 200
|
||||
|
||||
const updateField = <K extends keyof TemplateFormData>(field: K, value: TemplateFormData[K]) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }))
|
||||
@@ -302,6 +304,7 @@ export function TemplateDeploy({
|
||||
value={formData.tagline}
|
||||
onChange={(e) => updateField('tagline', e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
className={cn(formData.tagline.length > 200 && 'border-[var(--text-error)]')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { RepeatIcon, SplitIcon } from 'lucide-react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import {
|
||||
Popover,
|
||||
@@ -349,14 +351,24 @@ const getCaretViewportPosition = (
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a tag icon with background color
|
||||
* Renders a tag icon with background color - can use either a React icon component or a letter
|
||||
*/
|
||||
const TagIcon: React.FC<{ icon: string; color: string }> = ({ icon, color }) => (
|
||||
const TagIcon: React.FC<{
|
||||
icon: string | React.ComponentType<{ className?: string }>
|
||||
color: string
|
||||
}> = ({ icon, color }) => (
|
||||
<div
|
||||
className='flex h-[14px] w-[14px] flex-shrink-0 items-center justify-center rounded'
|
||||
style={{ backgroundColor: color }}
|
||||
style={{ background: color }}
|
||||
>
|
||||
<span className='font-bold text-[10px] text-white'>{icon}</span>
|
||||
{typeof icon === 'string' ? (
|
||||
<span className='font-bold text-[10px] text-white'>{icon}</span>
|
||||
) : (
|
||||
(() => {
|
||||
const IconComponent = icon
|
||||
return <IconComponent className='h-[9px] w-[9px] text-white' />
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1385,7 +1397,17 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
blockColor = BLOCK_COLORS.PARALLEL
|
||||
}
|
||||
|
||||
const tagIcon = group.blockName.charAt(0).toUpperCase()
|
||||
// Use actual block icon if available, otherwise fall back to special icons for loop/parallel or first letter
|
||||
let tagIcon: string | React.ComponentType<{ className?: string }> = group.blockName
|
||||
.charAt(0)
|
||||
.toUpperCase()
|
||||
if (blockConfig?.icon) {
|
||||
tagIcon = blockConfig.icon
|
||||
} else if (group.blockType === 'loop') {
|
||||
tagIcon = RepeatIcon
|
||||
} else if (group.blockType === 'parallel') {
|
||||
tagIcon = SplitIcon
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={group.blockId}>
|
||||
|
||||
@@ -298,6 +298,8 @@ export function Terminal() {
|
||||
setOutputPanelWidth,
|
||||
openOnRun,
|
||||
setOpenOnRun,
|
||||
wrapText,
|
||||
setWrapText,
|
||||
setHasHydrated,
|
||||
} = useTerminalStore()
|
||||
const isExpanded = useTerminalStore((state) => state.terminalHeight > NEAR_MIN_THRESHOLD)
|
||||
@@ -312,7 +314,6 @@ export function Terminal() {
|
||||
const exportConsoleCSV = useTerminalConsoleStore((state) => state.exportConsoleCSV)
|
||||
const [selectedEntry, setSelectedEntry] = useState<ConsoleEntry | null>(null)
|
||||
const [isToggling, setIsToggling] = useState(false)
|
||||
const [wrapText, setWrapText] = useState(true)
|
||||
const [showCopySuccess, setShowCopySuccess] = useState(false)
|
||||
const [showInput, setShowInput] = useState(false)
|
||||
const [autoSelectEnabled, setAutoSelectEnabled] = useState(true)
|
||||
@@ -1528,7 +1529,7 @@ export function Terminal() {
|
||||
showCheck
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setWrapText((prev) => !prev)
|
||||
setWrapText(!wrapText)
|
||||
}}
|
||||
>
|
||||
<span>Wrap text</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useCallback } from 'react'
|
||||
import { ArrowLeftRight, ArrowUpDown, Circle, CircleOff, LogOut } from 'lucide-react'
|
||||
import { Button, Duplicate, Tooltip, Trash2 } from '@/components/emcn'
|
||||
import { Button, Copy, Tooltip, Trash2 } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
@@ -119,7 +119,7 @@ export const ActionBar = memo(
|
||||
className='hover:!text-[var(--text-inverse)] h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)]'
|
||||
disabled={disabled}
|
||||
>
|
||||
<Duplicate className='h-[11px] w-[11px]' />
|
||||
<Copy className='h-[11px] w-[11px]' />
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>{getTooltipMessage('Duplicate Block')}</Tooltip.Content>
|
||||
|
||||
@@ -703,12 +703,12 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
const colorClasses = isError ? '!bg-red-400 dark:!bg-red-500' : '!bg-[var(--surface-12)]'
|
||||
|
||||
const positionClasses = {
|
||||
left: '!left-[-7px] !h-5 !w-[7px] !rounded-l-[2px] !rounded-r-none hover:!left-[-10px] hover:!w-[10px] hover:!rounded-l-full',
|
||||
left: '!left-[-8px] !h-5 !w-[7px] !rounded-l-[2px] !rounded-r-none hover:!left-[-11px] hover:!w-[10px] hover:!rounded-l-full',
|
||||
right:
|
||||
'!right-[-7px] !h-5 !w-[7px] !rounded-r-[2px] !rounded-l-none hover:!right-[-10px] hover:!w-[10px] hover:!rounded-r-full',
|
||||
top: '!top-[-7px] !h-[7px] !w-5 !rounded-t-[2px] !rounded-b-none hover:!top-[-10px] hover:!h-[10px] hover:!rounded-t-full',
|
||||
'!right-[-8px] !h-5 !w-[7px] !rounded-r-[2px] !rounded-l-none hover:!right-[-11px] hover:!w-[10px] hover:!rounded-r-full',
|
||||
top: '!top-[-8px] !h-[7px] !w-5 !rounded-t-[2px] !rounded-b-none hover:!top-[-11px] hover:!h-[10px] hover:!rounded-t-full',
|
||||
bottom:
|
||||
'!bottom-[-7px] !h-[7px] !w-5 !rounded-b-[2px] !rounded-t-none hover:!bottom-[-10px] hover:!h-[10px] hover:!rounded-b-full',
|
||||
'!bottom-[-8px] !h-[7px] !w-5 !rounded-b-[2px] !rounded-t-none hover:!bottom-[-11px] hover:!h-[10px] hover:!rounded-b-full',
|
||||
}
|
||||
|
||||
return cn(baseClasses, colorClasses, positionClasses[position])
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useBrandConfig } from '@/lib/branding/branding'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getTriggersForSidebar, hasTriggerCapability } from '@/lib/workflows/triggers/trigger-utils'
|
||||
import { searchItems } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-utils'
|
||||
import { SIDEBAR_SCROLL_EVENT } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar'
|
||||
import { getAllBlocks } from '@/blocks'
|
||||
|
||||
interface SearchModalProps {
|
||||
@@ -430,6 +431,12 @@ export function SearchModal({
|
||||
window.open(item.href, '_blank', 'noopener,noreferrer')
|
||||
} else {
|
||||
router.push(item.href)
|
||||
// Scroll to the workflow in the sidebar after navigation
|
||||
if (item.type === 'workflow') {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: item.id } })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
useItemDrag,
|
||||
useItemRename,
|
||||
} from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks'
|
||||
import { SIDEBAR_SCROLL_EVENT } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar'
|
||||
import { useDeleteFolder, useDuplicateFolder } from '@/app/workspace/[workspaceId]/w/hooks'
|
||||
import { useCreateFolder, useUpdateFolder } from '@/hooks/queries/folders'
|
||||
import { useCreateWorkflow } from '@/hooks/queries/workflows'
|
||||
@@ -87,6 +88,10 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) {
|
||||
|
||||
if (result.id) {
|
||||
router.push(`/workspace/${workspaceId}/w/${result.id}`)
|
||||
// Scroll to the newly created workflow
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: result.id } })
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
// Error already handled by mutation's onError callback
|
||||
@@ -100,11 +105,17 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) {
|
||||
*/
|
||||
const handleCreateFolderInFolder = useCallback(async () => {
|
||||
try {
|
||||
await createFolderMutation.mutateAsync({
|
||||
const result = await createFolderMutation.mutateAsync({
|
||||
workspaceId,
|
||||
name: 'New Folder',
|
||||
parentId: folder.id,
|
||||
})
|
||||
if (result.id) {
|
||||
// Scroll to the newly created folder
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: result.id } })
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to create folder:', error)
|
||||
}
|
||||
|
||||
@@ -145,20 +145,18 @@ export function WorkflowList({
|
||||
)
|
||||
|
||||
/**
|
||||
* Auto-expand folders and select the active workflow
|
||||
* Auto-expand folders and select active workflow.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!workflowId || isLoading || foldersLoading) return
|
||||
|
||||
// Expand folder path
|
||||
// Expand folder path to reveal workflow
|
||||
if (activeWorkflowFolderId) {
|
||||
const folderPath = getFolderPath(activeWorkflowFolderId)
|
||||
for (const folder of folderPath) {
|
||||
setExpanded(folder.id, true)
|
||||
}
|
||||
folderPath.forEach((folder) => setExpanded(folder.id, true))
|
||||
}
|
||||
|
||||
// Auto-select active workflow if not already selected
|
||||
// Select workflow if not already selected
|
||||
const { selectedWorkflows, selectOnly } = useFolderStore.getState()
|
||||
if (!selectedWorkflows.has(workflowId)) {
|
||||
selectOnly(workflowId)
|
||||
|
||||
@@ -34,9 +34,13 @@ import { useSearchModalStore } from '@/stores/search-modal/store'
|
||||
import { MIN_SIDEBAR_WIDTH, useSidebarStore } from '@/stores/sidebar/store'
|
||||
|
||||
const logger = createLogger('Sidebar')
|
||||
// Feature flag: Billing usage indicator visibility (matches legacy sidebar behavior)
|
||||
|
||||
/** Feature flag for billing usage indicator visibility */
|
||||
const isBillingEnabled = isTruthy(getEnv('NEXT_PUBLIC_BILLING_ENABLED'))
|
||||
|
||||
/** Event name for sidebar scroll operations - centralized for consistency */
|
||||
export const SIDEBAR_SCROLL_EVENT = 'sidebar-scroll-to-item'
|
||||
|
||||
/**
|
||||
* Sidebar component with resizable width that persists across page refreshes.
|
||||
*
|
||||
@@ -60,63 +64,79 @@ export function Sidebar() {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Session data
|
||||
const { data: sessionData, isPending: sessionLoading } = useSession()
|
||||
|
||||
// Sidebar state - use store's hydration tracking to prevent SSR mismatch
|
||||
/**
|
||||
* Sidebar state from store with hydration tracking to prevent SSR mismatch.
|
||||
* Uses default (expanded) state until hydrated.
|
||||
*/
|
||||
const hasHydrated = useSidebarStore((state) => state._hasHydrated)
|
||||
const isCollapsedStore = useSidebarStore((state) => state.isCollapsed)
|
||||
const setIsCollapsed = useSidebarStore((state) => state.setIsCollapsed)
|
||||
const setSidebarWidth = useSidebarStore((state) => state.setSidebarWidth)
|
||||
|
||||
// Use default (expanded) state until hydrated to prevent hydration mismatch
|
||||
const isCollapsed = hasHydrated ? isCollapsedStore : false
|
||||
|
||||
// Determine if we're on a workflow page (only workflow pages allow collapse and resize)
|
||||
const isOnWorkflowPage = !!workflowId
|
||||
|
||||
// Import state
|
||||
const [isImporting, setIsImporting] = useState(false)
|
||||
|
||||
// Workspace import input ref
|
||||
const workspaceFileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
// Workspace import hook
|
||||
const { isImporting: isImportingWorkspace, handleImportWorkspace: importWorkspace } =
|
||||
useImportWorkspace()
|
||||
const { handleExportWorkspace: exportWorkspace } = useExportWorkspace()
|
||||
|
||||
// Workspace export hook
|
||||
const { isExporting: isExportingWorkspace, handleExportWorkspace: exportWorkspace } =
|
||||
useExportWorkspace()
|
||||
|
||||
// Workspace popover state
|
||||
const [isWorkspaceMenuOpen, setIsWorkspaceMenuOpen] = useState(false)
|
||||
|
||||
// Footer navigation modal state
|
||||
const [isHelpModalOpen, setIsHelpModalOpen] = useState(false)
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false)
|
||||
|
||||
// Listen for external events to open help modal
|
||||
/** Listens for external events to open help modal */
|
||||
useEffect(() => {
|
||||
const handleOpenHelpModal = () => setIsHelpModalOpen(true)
|
||||
window.addEventListener('open-help-modal', handleOpenHelpModal)
|
||||
return () => window.removeEventListener('open-help-modal', handleOpenHelpModal)
|
||||
}, [])
|
||||
|
||||
// Global search modal state
|
||||
/** Listens for scroll events and scrolls items into view if off-screen */
|
||||
useEffect(() => {
|
||||
const handleScrollToItem = (e: CustomEvent<{ itemId: string }>) => {
|
||||
const { itemId } = e.detail
|
||||
if (!itemId) return
|
||||
|
||||
const tryScroll = (retriesLeft: number) => {
|
||||
requestAnimationFrame(() => {
|
||||
const element = document.querySelector(`[data-item-id="${itemId}"]`)
|
||||
const container = scrollContainerRef.current
|
||||
|
||||
if (!element || !container) {
|
||||
if (retriesLeft > 0) tryScroll(retriesLeft - 1)
|
||||
return
|
||||
}
|
||||
|
||||
const { top: elTop, bottom: elBottom } = element.getBoundingClientRect()
|
||||
const { top: ctTop, bottom: ctBottom } = container.getBoundingClientRect()
|
||||
|
||||
if (elBottom <= ctTop || elTop >= ctBottom) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
tryScroll(10)
|
||||
}
|
||||
window.addEventListener(SIDEBAR_SCROLL_EVENT, handleScrollToItem as EventListener)
|
||||
return () =>
|
||||
window.removeEventListener(SIDEBAR_SCROLL_EVENT, handleScrollToItem as EventListener)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
isOpen: isSearchModalOpen,
|
||||
setOpen: setIsSearchModalOpen,
|
||||
open: openSearchModal,
|
||||
} = useSearchModalStore()
|
||||
|
||||
// Workspace management hook
|
||||
const {
|
||||
workspaces,
|
||||
activeWorkspace,
|
||||
isWorkspacesLoading,
|
||||
fetchWorkspaces,
|
||||
isWorkspaceValid,
|
||||
switchWorkspace,
|
||||
handleCreateWorkspace,
|
||||
isCreatingWorkspace,
|
||||
@@ -127,10 +147,8 @@ export function Sidebar() {
|
||||
sessionUserId: sessionData?.user?.id,
|
||||
})
|
||||
|
||||
// Sidebar resize hook
|
||||
const { handleMouseDown } = useSidebarResize()
|
||||
|
||||
// Workflow operations hook
|
||||
const {
|
||||
regularWorkflows,
|
||||
workflowsLoading,
|
||||
@@ -138,17 +156,14 @@ export function Sidebar() {
|
||||
handleCreateWorkflow: createWorkflow,
|
||||
} = useWorkflowOperations({ workspaceId })
|
||||
|
||||
// Folder operations hook
|
||||
const { isCreatingFolder, handleCreateFolder: createFolder } = useFolderOperations({
|
||||
workspaceId,
|
||||
})
|
||||
|
||||
// Duplicate workspace hook
|
||||
const { handleDuplicateWorkspace: duplicateWorkspace } = useDuplicateWorkspace({
|
||||
getWorkspaceId: () => workspaceId,
|
||||
})
|
||||
|
||||
// Prepare data for search modal
|
||||
const searchModalWorkflows = useMemo(
|
||||
() =>
|
||||
regularWorkflows.map((workflow) => ({
|
||||
@@ -172,7 +187,6 @@ export function Sidebar() {
|
||||
[workspaces, workspaceId]
|
||||
)
|
||||
|
||||
// Footer navigation items
|
||||
const footerNavigationItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -209,156 +223,85 @@ export function Sidebar() {
|
||||
[workspaceId]
|
||||
)
|
||||
|
||||
// Combined loading state
|
||||
const isLoading = workflowsLoading || sessionLoading
|
||||
const initialScrollDoneRef = useRef<string | null>(null)
|
||||
|
||||
// Ref to track active timeout IDs for cleanup
|
||||
const scrollTimeoutRef = useRef<number | null>(null)
|
||||
|
||||
/**
|
||||
* Scrolls an element into view if it's not already visible in the scroll container.
|
||||
* Uses a retry mechanism with cleanup to wait for the element to be rendered in the DOM.
|
||||
*
|
||||
* @param elementId - The ID of the element to scroll to
|
||||
* @param maxRetries - Maximum number of retry attempts (default: 10)
|
||||
*/
|
||||
const scrollToElement = useCallback(
|
||||
(elementId: string, maxRetries = 10) => {
|
||||
// Clear any existing timeout
|
||||
if (scrollTimeoutRef.current !== null) {
|
||||
clearTimeout(scrollTimeoutRef.current)
|
||||
scrollTimeoutRef.current = null
|
||||
}
|
||||
|
||||
let attempts = 0
|
||||
|
||||
const tryScroll = () => {
|
||||
attempts++
|
||||
const element = document.querySelector(`[data-item-id="${elementId}"]`)
|
||||
const container = scrollContainerRef.current
|
||||
|
||||
if (element && container) {
|
||||
const elementRect = element.getBoundingClientRect()
|
||||
const containerRect = container.getBoundingClientRect()
|
||||
|
||||
// Check if element is not fully visible in the container
|
||||
const isAboveView = elementRect.top < containerRect.top
|
||||
const isBelowView = elementRect.bottom > containerRect.bottom
|
||||
|
||||
if (isAboveView || isBelowView) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
||||
}
|
||||
scrollTimeoutRef.current = null
|
||||
} else if (attempts < maxRetries) {
|
||||
// Element not in DOM yet, retry after a short delay
|
||||
scrollTimeoutRef.current = window.setTimeout(tryScroll, 50)
|
||||
} else {
|
||||
scrollTimeoutRef.current = null
|
||||
}
|
||||
}
|
||||
|
||||
// Start the scroll attempt after a small delay to ensure rendering.
|
||||
scrollTimeoutRef.current = window.setTimeout(tryScroll, 50)
|
||||
},
|
||||
[scrollContainerRef]
|
||||
)
|
||||
|
||||
// Cleanup timeouts on unmount
|
||||
/** Scrolls to active workflow on initial load or workspace switch */
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (scrollTimeoutRef.current !== null) {
|
||||
clearTimeout(scrollTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
if (!workflowId || workflowsLoading || initialScrollDoneRef.current === workflowId) return
|
||||
initialScrollDoneRef.current = workflowId
|
||||
requestAnimationFrame(() => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: workflowId } })
|
||||
)
|
||||
})
|
||||
}, [workflowId, workflowsLoading])
|
||||
|
||||
/**
|
||||
* Force sidebar to minimum width and ensure it's expanded when not on a workflow page
|
||||
*/
|
||||
/** Forces sidebar to minimum width and ensures it's expanded when not on a workflow page */
|
||||
useEffect(() => {
|
||||
if (!isOnWorkflowPage) {
|
||||
// Ensure sidebar is always expanded on non-workflow pages
|
||||
if (isCollapsed) {
|
||||
setIsCollapsed(false)
|
||||
}
|
||||
// Force sidebar to minimum width
|
||||
setSidebarWidth(MIN_SIDEBAR_WIDTH)
|
||||
}
|
||||
}, [isOnWorkflowPage, isCollapsed, setIsCollapsed, setSidebarWidth])
|
||||
|
||||
/**
|
||||
* Handle create workflow - creates workflow and scrolls to it
|
||||
*/
|
||||
/** Creates a workflow and scrolls to it */
|
||||
const handleCreateWorkflow = useCallback(async () => {
|
||||
const workflowId = await createWorkflow()
|
||||
if (workflowId) {
|
||||
scrollToElement(workflowId)
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: workflowId } })
|
||||
)
|
||||
}
|
||||
}, [createWorkflow, scrollToElement])
|
||||
}, [createWorkflow])
|
||||
|
||||
/**
|
||||
* Handle create folder - creates folder and scrolls to it
|
||||
*/
|
||||
/** Creates a folder and scrolls to it */
|
||||
const handleCreateFolder = useCallback(async () => {
|
||||
const folderId = await createFolder()
|
||||
if (folderId) {
|
||||
scrollToElement(folderId)
|
||||
window.dispatchEvent(new CustomEvent(SIDEBAR_SCROLL_EVENT, { detail: { itemId: folderId } }))
|
||||
}
|
||||
}, [createFolder, scrollToElement])
|
||||
}, [createFolder])
|
||||
|
||||
/**
|
||||
* Handle import workflow button click - triggers file input
|
||||
*/
|
||||
/** Triggers file input for workflow import */
|
||||
const handleImportWorkflow = useCallback(() => {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.click()
|
||||
}
|
||||
fileInputRef.current?.click()
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Handle workspace switch from popover menu
|
||||
*/
|
||||
/** Handles workspace switch from popover menu */
|
||||
const handleWorkspaceSwitch = useCallback(
|
||||
async (workspace: { id: string; name: string; ownerId: string; role?: string }) => {
|
||||
if (workspace.id === workspaceId) {
|
||||
setIsWorkspaceMenuOpen(false)
|
||||
return
|
||||
}
|
||||
|
||||
await switchWorkspace(workspace)
|
||||
setIsWorkspaceMenuOpen(false)
|
||||
},
|
||||
[workspaceId, switchWorkspace]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle sidebar collapse toggle
|
||||
*/
|
||||
/** Toggles sidebar collapse state */
|
||||
const handleToggleCollapse = useCallback(() => {
|
||||
setIsCollapsed(!isCollapsed)
|
||||
}, [isCollapsed, setIsCollapsed])
|
||||
|
||||
/**
|
||||
* Handle click on sidebar elements to revert to active workflow selection
|
||||
*/
|
||||
/** Reverts to active workflow selection when clicking sidebar background */
|
||||
const handleSidebarClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLElement>) => {
|
||||
const target = e.target as HTMLElement
|
||||
// Revert to active workflow selection if clicking on sidebar background, header, or search area
|
||||
// But not on interactive elements like buttons or links
|
||||
if (target.tagName === 'BUTTON' || target.closest('button, [role="button"], a')) {
|
||||
return
|
||||
}
|
||||
|
||||
const { selectOnly, clearSelection } = useFolderStore.getState()
|
||||
workflowId ? selectOnly(workflowId) : clearSelection()
|
||||
},
|
||||
[workflowId]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle workspace rename
|
||||
*/
|
||||
/** Renames a workspace */
|
||||
const handleRenameWorkspace = useCallback(
|
||||
async (workspaceIdToRename: string, newName: string) => {
|
||||
await updateWorkspaceName(workspaceIdToRename, newName)
|
||||
@@ -366,9 +309,7 @@ export function Sidebar() {
|
||||
[updateWorkspaceName]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle workspace delete
|
||||
*/
|
||||
/** Deletes a workspace */
|
||||
const handleDeleteWorkspace = useCallback(
|
||||
async (workspaceIdToDelete: string) => {
|
||||
const workspaceToDelete = workspaces.find((w) => w.id === workspaceIdToDelete)
|
||||
@@ -379,9 +320,7 @@ export function Sidebar() {
|
||||
[workspaces, confirmDeleteWorkspace]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle workspace duplicate
|
||||
*/
|
||||
/** Duplicates a workspace */
|
||||
const handleDuplicateWorkspace = useCallback(
|
||||
async (_workspaceIdToDuplicate: string, workspaceName: string) => {
|
||||
await duplicateWorkspace(workspaceName)
|
||||
@@ -389,9 +328,7 @@ export function Sidebar() {
|
||||
[duplicateWorkspace]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle workspace export
|
||||
*/
|
||||
/** Exports a workspace */
|
||||
const handleExportWorkspace = useCallback(
|
||||
async (workspaceIdToExport: string, workspaceName: string) => {
|
||||
await exportWorkspace(workspaceIdToExport, workspaceName)
|
||||
@@ -399,18 +336,12 @@ export function Sidebar() {
|
||||
[exportWorkspace]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle workspace import button click
|
||||
*/
|
||||
/** Triggers file input for workspace import */
|
||||
const handleImportWorkspace = useCallback(() => {
|
||||
if (workspaceFileInputRef.current) {
|
||||
workspaceFileInputRef.current.click()
|
||||
}
|
||||
workspaceFileInputRef.current?.click()
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Handle workspace import file change
|
||||
*/
|
||||
/** Handles workspace import file selection */
|
||||
const handleWorkspaceFileChange = useCallback(
|
||||
async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = event.target.files
|
||||
@@ -419,7 +350,6 @@ export function Sidebar() {
|
||||
const zipFile = files[0]
|
||||
await importWorkspace(zipFile)
|
||||
|
||||
// Reset file input
|
||||
if (event.target) {
|
||||
event.target.value = ''
|
||||
}
|
||||
@@ -427,12 +357,7 @@ export function Sidebar() {
|
||||
[importWorkspace]
|
||||
)
|
||||
|
||||
/**
|
||||
* Resolve a workspace id from either params or the current URL path.
|
||||
*
|
||||
* This mirrors existing behavior but is wrapped in a helper to keep command
|
||||
* handlers small and focused.
|
||||
*/
|
||||
/** Resolves workspace ID from params or URL path */
|
||||
const resolveWorkspaceIdFromPath = useCallback((): string | undefined => {
|
||||
if (workspaceId) return workspaceId
|
||||
if (typeof window === 'undefined') return undefined
|
||||
@@ -444,12 +369,7 @@ export function Sidebar() {
|
||||
return parts[idx + 1]
|
||||
}, [workspaceId])
|
||||
|
||||
/**
|
||||
* Register global sidebar commands using the central commands registry.
|
||||
*
|
||||
* Only commands declared in the registry can be registered here. The
|
||||
* registry owns ids and shortcut strings; this component supplies handlers.
|
||||
*/
|
||||
/** Registers global sidebar commands with the central commands registry */
|
||||
useRegisterGlobalCommands(() =>
|
||||
createCommands([
|
||||
{
|
||||
|
||||
@@ -193,6 +193,7 @@ async function runWorkflowExecution({
|
||||
const deployedData = await loadDeployedWorkflowState(payload.workflowId)
|
||||
|
||||
const blocks = deployedData.blocks
|
||||
const { deploymentVersionId } = deployedData
|
||||
logger.info(`[${requestId}] Loaded deployed workflow ${payload.workflowId}`)
|
||||
|
||||
if (payload.blockId) {
|
||||
@@ -233,6 +234,7 @@ async function runWorkflowExecution({
|
||||
userId: actorUserId,
|
||||
workspaceId: workflowRecord.workspaceId || '',
|
||||
variables: variables || {},
|
||||
deploymentVersionId,
|
||||
})
|
||||
|
||||
const metadata: ExecutionMetadata = {
|
||||
|
||||
@@ -138,18 +138,26 @@ async function executeWebhookJobInternal(
|
||||
requestId
|
||||
)
|
||||
|
||||
// Track deploymentVersionId at function scope so it's available in catch block
|
||||
let deploymentVersionId: string | undefined
|
||||
|
||||
try {
|
||||
const workflowData =
|
||||
payload.executionTarget === 'live'
|
||||
? await loadWorkflowFromNormalizedTables(payload.workflowId)
|
||||
: await loadDeployedWorkflowState(payload.workflowId)
|
||||
const useDraftState = payload.executionTarget === 'live'
|
||||
const workflowData = useDraftState
|
||||
? await loadWorkflowFromNormalizedTables(payload.workflowId)
|
||||
: await loadDeployedWorkflowState(payload.workflowId)
|
||||
if (!workflowData) {
|
||||
throw new Error(
|
||||
`Workflow state not found. The workflow may not be ${payload.executionTarget === 'live' ? 'saved' : 'deployed'} or the deployment data may be corrupted.`
|
||||
`Workflow state not found. The workflow may not be ${useDraftState ? 'saved' : 'deployed'} or the deployment data may be corrupted.`
|
||||
)
|
||||
}
|
||||
|
||||
const { blocks, edges, loops, parallels } = workflowData
|
||||
// Only deployed executions have a deployment version ID
|
||||
deploymentVersionId =
|
||||
!useDraftState && 'deploymentVersionId' in workflowData
|
||||
? (workflowData.deploymentVersionId as string)
|
||||
: undefined
|
||||
|
||||
const wfRows = await db
|
||||
.select({ workspaceId: workflowTable.workspaceId, variables: workflowTable.variables })
|
||||
@@ -229,6 +237,13 @@ async function executeWebhookJobInternal(
|
||||
useDraftState: false,
|
||||
startTime: new Date().toISOString(),
|
||||
isClientSession: false,
|
||||
workflowStateOverride: {
|
||||
blocks,
|
||||
edges,
|
||||
loops: loops || {},
|
||||
parallels: parallels || {},
|
||||
deploymentVersionId,
|
||||
},
|
||||
}
|
||||
|
||||
const snapshot = new ExecutionSnapshot(
|
||||
@@ -280,6 +295,18 @@ async function executeWebhookJobInternal(
|
||||
// No changes to process
|
||||
logger.info(`[${requestId}] No Airtable changes to process`)
|
||||
|
||||
// Start logging session so the complete call has a log entry to update
|
||||
await loggingSession.safeStart({
|
||||
userId: payload.userId,
|
||||
workspaceId: workspaceId || '',
|
||||
variables: {},
|
||||
triggerData: {
|
||||
isTest: payload.testMode === true,
|
||||
executionTarget: payload.executionTarget || 'deployed',
|
||||
},
|
||||
deploymentVersionId,
|
||||
})
|
||||
|
||||
await loggingSession.safeComplete({
|
||||
endedAt: new Date().toISOString(),
|
||||
totalDurationMs: 0,
|
||||
@@ -325,6 +352,19 @@ async function executeWebhookJobInternal(
|
||||
|
||||
if (!input && payload.provider === 'whatsapp') {
|
||||
logger.info(`[${requestId}] No messages in WhatsApp payload, skipping execution`)
|
||||
|
||||
// Start logging session so the complete call has a log entry to update
|
||||
await loggingSession.safeStart({
|
||||
userId: payload.userId,
|
||||
workspaceId: workspaceId || '',
|
||||
variables: {},
|
||||
triggerData: {
|
||||
isTest: payload.testMode === true,
|
||||
executionTarget: payload.executionTarget || 'deployed',
|
||||
},
|
||||
deploymentVersionId,
|
||||
})
|
||||
|
||||
await loggingSession.safeComplete({
|
||||
endedAt: new Date().toISOString(),
|
||||
totalDurationMs: 0,
|
||||
@@ -444,6 +484,13 @@ async function executeWebhookJobInternal(
|
||||
useDraftState: false,
|
||||
startTime: new Date().toISOString(),
|
||||
isClientSession: false,
|
||||
workflowStateOverride: {
|
||||
blocks,
|
||||
edges,
|
||||
loops: loops || {},
|
||||
parallels: parallels || {},
|
||||
deploymentVersionId,
|
||||
},
|
||||
}
|
||||
|
||||
const snapshot = new ExecutionSnapshot(metadata, workflow, input || {}, workflowVariables, [])
|
||||
@@ -495,7 +542,6 @@ async function executeWebhookJobInternal(
|
||||
})
|
||||
|
||||
try {
|
||||
// Ensure logging session is started (safe to call multiple times)
|
||||
await loggingSession.safeStart({
|
||||
userId: payload.userId,
|
||||
workspaceId: '', // May not be available for early errors
|
||||
@@ -504,6 +550,7 @@ async function executeWebhookJobInternal(
|
||||
isTest: payload.testMode === true,
|
||||
executionTarget: payload.executionTarget || 'deployed',
|
||||
},
|
||||
deploymentVersionId, // Pass if available (undefined for early errors)
|
||||
})
|
||||
|
||||
const executionResult = (error?.executionResult as ExecutionResult | undefined) || {
|
||||
|
||||
@@ -32,11 +32,23 @@ export const BrowserUseBlock: BlockConfig<BrowserUseResponse> = {
|
||||
title: 'Model',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'gpt-4o', id: 'gpt-4o' },
|
||||
{ label: 'gemini-2.0-flash', id: 'gemini-2.0-flash' },
|
||||
{ label: 'gemini-2.0-flash-lite', id: 'gemini-2.0-flash-lite' },
|
||||
{ label: 'claude-3-7-sonnet-20250219', id: 'claude-3-7-sonnet-20250219' },
|
||||
{ label: 'llama-4-maverick-17b-128e-instruct', id: 'llama-4-maverick-17b-128e-instruct' },
|
||||
{ label: 'Browser Use LLM', id: 'browser-use-llm' },
|
||||
{ label: 'GPT-4o', id: 'gpt-4o' },
|
||||
{ label: 'GPT-4o Mini', id: 'gpt-4o-mini' },
|
||||
{ label: 'GPT-4.1', id: 'gpt-4.1' },
|
||||
{ label: 'GPT-4.1 Mini', id: 'gpt-4.1-mini' },
|
||||
{ label: 'O3', id: 'o3' },
|
||||
{ label: 'O4 Mini', id: 'o4-mini' },
|
||||
{ label: 'Gemini 2.5 Flash', id: 'gemini-2.5-flash' },
|
||||
{ label: 'Gemini 2.5 Pro', id: 'gemini-2.5-pro' },
|
||||
{ label: 'Gemini 3 Pro Preview', id: 'gemini-3-pro-preview' },
|
||||
{ label: 'Gemini Flash Latest', id: 'gemini-flash-latest' },
|
||||
{ label: 'Gemini Flash Lite Latest', id: 'gemini-flash-lite-latest' },
|
||||
{ label: 'Claude 3.7 Sonnet', id: 'claude-3-7-sonnet-20250219' },
|
||||
{ label: 'Claude Sonnet 4', id: 'claude-sonnet-4-20250514' },
|
||||
{ label: 'Claude Sonnet 4.5', id: 'claude-sonnet-4-5-20250929' },
|
||||
{ label: 'Claude Opus 4.5', id: 'claude-opus-4-5-20251101' },
|
||||
{ label: 'Llama 4 Maverick', id: 'llama-4-maverick-17b-128e-instruct' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -110,14 +110,14 @@ export const ParallelBlock: BlockConfig<ToolResponse> = {
|
||||
title: 'Processor',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Lite ($5/1K)', id: 'lite' },
|
||||
{ label: 'Base ($10/1K)', id: 'base' },
|
||||
{ label: 'Core ($25/1K)', id: 'core' },
|
||||
{ label: 'Core 2x ($50/1K)', id: 'core2x' },
|
||||
{ label: 'Pro ($100/1K)', id: 'pro' },
|
||||
{ label: 'Ultra ($300/1K)', id: 'ultra' },
|
||||
{ label: 'Ultra 2x ($600/1K)', id: 'ultra2x' },
|
||||
{ label: 'Ultra 4x ($1,200/1K)', id: 'ultra4x' },
|
||||
{ label: 'Lite', id: 'lite' },
|
||||
{ label: 'Base', id: 'base' },
|
||||
{ label: 'Core', id: 'core' },
|
||||
{ label: 'Core 2x', id: 'core2x' },
|
||||
{ label: 'Pro', id: 'pro' },
|
||||
{ label: 'Ultra', id: 'ultra' },
|
||||
{ label: 'Ultra 2x', id: 'ultra2x' },
|
||||
{ label: 'Ultra 4x', id: 'ultra4x' },
|
||||
],
|
||||
value: () => 'base',
|
||||
condition: { field: 'operation', value: ['search', 'deep_research'] },
|
||||
|
||||
@@ -48,8 +48,119 @@ export const StagehandBlock: BlockConfig<StagehandExtractResponse> = {
|
||||
type: 'code',
|
||||
placeholder: 'Enter JSON Schema...',
|
||||
language: 'json',
|
||||
generationType: 'json-schema',
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert programmer specializing in creating JSON schemas for web scraping and data extraction.
|
||||
Generate ONLY the JSON schema based on the user's request.
|
||||
The output MUST be a single, valid JSON object, starting with { and ending with }.
|
||||
The JSON object MUST have the following top-level properties: 'name' (string), 'description' (string), 'strict' (boolean, usually true), and 'schema' (object).
|
||||
The 'schema' object must define the structure and MUST contain 'type': 'object', 'properties': {...}, 'additionalProperties': false, and 'required': [...].
|
||||
Inside 'properties', use standard JSON Schema properties (type, description, enum, items for arrays, etc.).
|
||||
|
||||
Current schema: {context}
|
||||
|
||||
Do not include any explanations, markdown formatting, or other text outside the JSON object.
|
||||
|
||||
Valid Schema Examples:
|
||||
|
||||
Example 1 (Product Extraction):
|
||||
{
|
||||
"name": "product_info",
|
||||
"description": "Extracts product information from an e-commerce page",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The product name"
|
||||
},
|
||||
"price": {
|
||||
"type": "string",
|
||||
"description": "The product price"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The product description"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "price"]
|
||||
}
|
||||
}
|
||||
|
||||
Example 2 (Article Extraction):
|
||||
{
|
||||
"name": "article_content",
|
||||
"description": "Extracts article content from a news or blog page",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The article headline"
|
||||
},
|
||||
"author": {
|
||||
"type": "string",
|
||||
"description": "The article author"
|
||||
},
|
||||
"publishDate": {
|
||||
"type": "string",
|
||||
"description": "The publication date"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The main article text"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["title", "content"]
|
||||
}
|
||||
}
|
||||
|
||||
Example 3 (List Extraction):
|
||||
{
|
||||
"name": "search_results",
|
||||
"description": "Extracts search results or list items from a page",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"description": "List of extracted items",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Item title"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "Item URL"
|
||||
},
|
||||
"snippet": {
|
||||
"type": "string",
|
||||
"description": "Brief description or snippet"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["title"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["items"]
|
||||
}
|
||||
}
|
||||
`,
|
||||
placeholder: 'Describe what data you want to extract from the webpage...',
|
||||
generationType: 'json-schema',
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
|
||||
@@ -49,7 +49,118 @@ export const StagehandAgentBlock: BlockConfig<StagehandAgentResponse> = {
|
||||
type: 'code',
|
||||
placeholder: 'Enter JSON Schema...',
|
||||
language: 'json',
|
||||
generationType: 'json-schema',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert programmer specializing in creating JSON schemas for web automation agents.
|
||||
Generate ONLY the JSON schema based on the user's request.
|
||||
The output MUST be a single, valid JSON object, starting with { and ending with }.
|
||||
The JSON object MUST have the following top-level properties: 'name' (string), 'description' (string), 'strict' (boolean, usually true), and 'schema' (object).
|
||||
The 'schema' object must define the structure and MUST contain 'type': 'object', 'properties': {...}, 'additionalProperties': false, and 'required': [...].
|
||||
Inside 'properties', use standard JSON Schema properties (type, description, enum, items for arrays, etc.).
|
||||
|
||||
Current schema: {context}
|
||||
|
||||
Do not include any explanations, markdown formatting, or other text outside the JSON object.
|
||||
|
||||
Valid Schema Examples:
|
||||
|
||||
Example 1 (Login Result):
|
||||
{
|
||||
"name": "login_result",
|
||||
"description": "Result of a login task performed by the agent",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the login was successful"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"description": "The username that was logged in"
|
||||
},
|
||||
"dashboardUrl": {
|
||||
"type": "string",
|
||||
"description": "The URL of the dashboard after login"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["success"]
|
||||
}
|
||||
}
|
||||
|
||||
Example 2 (Form Submission):
|
||||
{
|
||||
"name": "form_submission_result",
|
||||
"description": "Result of submitting a form",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"submitted": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the form was submitted"
|
||||
},
|
||||
"confirmationNumber": {
|
||||
"type": "string",
|
||||
"description": "Confirmation or reference number if provided"
|
||||
},
|
||||
"errorMessage": {
|
||||
"type": "string",
|
||||
"description": "Error message if submission failed"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["submitted"]
|
||||
}
|
||||
}
|
||||
|
||||
Example 3 (Data Collection):
|
||||
{
|
||||
"name": "collected_data",
|
||||
"description": "Data collected by the agent from multiple pages",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"description": "List of collected items",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Item name"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Item value or content"
|
||||
},
|
||||
"sourceUrl": {
|
||||
"type": "string",
|
||||
"description": "URL where the item was found"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"totalCount": {
|
||||
"type": "number",
|
||||
"description": "Total number of items collected"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["items"]
|
||||
}
|
||||
}
|
||||
`,
|
||||
placeholder: 'Describe what output format you expect from the agent task...',
|
||||
generationType: 'json-schema',
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
|
||||
@@ -304,15 +304,44 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
|
||||
|
||||
outputs: {
|
||||
transcript: { type: 'string', description: 'Full transcribed text' },
|
||||
segments: { type: 'array', description: 'Timestamped segments with speaker labels' },
|
||||
segments: {
|
||||
type: 'array',
|
||||
description: 'Timestamped segments with speaker labels',
|
||||
condition: { field: 'timestamps', value: 'none', not: true },
|
||||
},
|
||||
language: { type: 'string', description: 'Detected or specified language' },
|
||||
duration: { type: 'number', description: 'Audio duration in seconds' },
|
||||
confidence: {
|
||||
type: 'number',
|
||||
description: 'Overall confidence score (Deepgram, AssemblyAI only)',
|
||||
description: 'Overall confidence score',
|
||||
condition: { field: 'provider', value: ['deepgram', 'assemblyai', 'gemini'] },
|
||||
},
|
||||
sentiment: {
|
||||
type: 'array',
|
||||
description: 'Sentiment analysis results',
|
||||
condition: {
|
||||
field: 'provider',
|
||||
value: 'assemblyai',
|
||||
and: { field: 'sentiment', value: true },
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
type: 'array',
|
||||
description: 'Detected entities',
|
||||
condition: {
|
||||
field: 'provider',
|
||||
value: 'assemblyai',
|
||||
and: { field: 'entityDetection', value: true },
|
||||
},
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
description: 'Auto-generated summary',
|
||||
condition: {
|
||||
field: 'provider',
|
||||
value: 'assemblyai',
|
||||
and: { field: 'summarization', value: true },
|
||||
},
|
||||
},
|
||||
sentiment: { type: 'array', description: 'Sentiment analysis results (AssemblyAI only)' },
|
||||
entities: { type: 'array', description: 'Detected entities (AssemblyAI only)' },
|
||||
summary: { type: 'string', description: 'Auto-generated summary (AssemblyAI only)' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
import { TranslateIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import {
|
||||
getAllModelProviders,
|
||||
getHostedModels,
|
||||
getProviderIcon,
|
||||
providers,
|
||||
} from '@/providers/utils'
|
||||
import { getHostedModels, getProviderIcon, providers } from '@/providers/utils'
|
||||
import { useProvidersStore } from '@/stores/providers/store'
|
||||
|
||||
const getCurrentOllamaModels = () => {
|
||||
return useProvidersStore.getState().providers.ollama.models
|
||||
}
|
||||
|
||||
const getTranslationPrompt = (
|
||||
targetLanguage: string
|
||||
) => `You are a highly skilled translator. Your task is to translate the given text into ${targetLanguage || 'English'} while:
|
||||
1. Preserving the original meaning and nuance
|
||||
2. Maintaining appropriate formality levels
|
||||
3. Adapting idioms and cultural references appropriately
|
||||
4. Preserving formatting and special characters
|
||||
5. Handling technical terms accurately
|
||||
|
||||
Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.`
|
||||
const getTranslationPrompt = (targetLanguage: string) =>
|
||||
`Translate the following text into ${targetLanguage || 'English'}. Output ONLY the translated text with no additional commentary, explanations, or notes.`
|
||||
|
||||
export const TranslateBlock: BlockConfig = {
|
||||
type: 'translate',
|
||||
@@ -123,19 +110,17 @@ export const TranslateBlock: BlockConfig = {
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['openai_chat', 'anthropic_chat', 'google_chat'],
|
||||
access: ['llm_chat'],
|
||||
config: {
|
||||
tool: (params: Record<string, any>) => {
|
||||
const model = params.model || 'gpt-4o'
|
||||
if (!model) {
|
||||
throw new Error('No model selected')
|
||||
}
|
||||
const tool = getAllModelProviders()[model]
|
||||
if (!tool) {
|
||||
throw new Error(`Invalid model selected: ${model}`)
|
||||
}
|
||||
return tool
|
||||
},
|
||||
tool: () => 'llm_chat',
|
||||
params: (params: Record<string, any>) => ({
|
||||
model: params.model,
|
||||
systemPrompt: getTranslationPrompt(params.targetLanguage || 'English'),
|
||||
context: params.context,
|
||||
apiKey: params.apiKey,
|
||||
azureEndpoint: params.azureEndpoint,
|
||||
azureApiVersion: params.azureApiVersion,
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user