writer 0.8.3rc21__py3-none-any.whl → 1.25.1rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- writer/ai/__init__.py +277 -28
- writer/app_runner.py +236 -32
- writer/app_templates/default/.wf/components-blueprints_blueprint-0-0decp3w5erhvl0nw.jsonl +11 -0
- writer/app_templates/default/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +22 -10
- writer/app_templates/default/.wf/components-root.jsonl +1 -1
- writer/app_templates/default/.wf/components-workflows_root.jsonl +1 -0
- writer/app_templates/default/.wf/components-workflows_workflow-0-lfltcky7l1fsm6j2.jsonl +1 -0
- writer/app_templates/default/.wf/metadata.json +1 -1
- writer/app_templates/default/README.md +3 -0
- writer/app_templates/default/main.py +8 -5
- writer/app_templates/default/requirements.txt +1 -0
- writer/app_templates/default/static/agent_builder_demo.png +0 -0
- writer/app_templates/hello/main.py +3 -0
- writer/auth.py +7 -2
- writer/autogen.py +26 -9
- writer/blocks/__init__.py +21 -1
- writer/blocks/addtostatelist.py +5 -4
- writer/blocks/apitrigger.py +45 -0
- writer/blocks/base_block.py +134 -14
- writer/blocks/changepage.py +1 -1
- writer/blocks/code.py +27 -11
- writer/blocks/crontrigger.py +49 -0
- writer/blocks/foreach.py +3 -3
- writer/blocks/httprequest.py +8 -24
- writer/blocks/ifelse.py +71 -0
- writer/blocks/logmessage.py +2 -2
- writer/blocks/returnvalue.py +2 -2
- writer/blocks/runblueprint.py +2 -2
- writer/blocks/setstate.py +5 -5
- writer/blocks/sharedblueprint.py +86 -0
- writer/blocks/writeraddchatmessage.py +45 -7
- writer/blocks/writeraddtokg.py +31 -4
- writer/blocks/writeraskkg.py +27 -34
- writer/blocks/writerchat.py +14 -9
- writer/blocks/writerchatreply.py +279 -0
- writer/blocks/writerchatreplywithtoolconfig.py +393 -0
- writer/blocks/writerclassification.py +42 -5
- writer/blocks/writercompletion.py +4 -4
- writer/blocks/writerfileapi.py +4 -4
- writer/blocks/writerinitchat.py +5 -4
- writer/blocks/writerkeyvaluestorage.py +106 -0
- writer/blocks/writernocodeapp.py +4 -4
- writer/blocks/writerparsepdf.py +8 -6
- writer/blocks/writerstructuredoutput.py +105 -0
- writer/blocks/writertoolcalling.py +106 -51
- writer/blocks/writervision.py +141 -0
- writer/blocks/writerwebsearch.py +175 -0
- writer/blueprints.py +715 -251
- writer/command_line.py +52 -16
- writer/core.py +200 -35
- writer/core_ui.py +4 -0
- writer/evaluator.py +38 -24
- writer/journal.py +227 -0
- writer/keyvalue_storage.py +93 -0
- writer/logs.py +277 -0
- writer/serve.py +402 -198
- writer/ss_types.py +41 -0
- writer/static/assets/BaseMarkdown-Wrvby5J8.js +1 -0
- writer/static/assets/BlueprintToolbar-BuXNRxWT.js +1 -0
- writer/static/assets/BlueprintToolbar-wpfX0jo_.css +1 -0
- writer/static/assets/BuilderApp-PTOI76jZ.js +8 -0
- writer/static/assets/BuilderApp-WimUfNZr.css +1 -0
- writer/static/assets/BuilderApplicationSelect-DXzy4e_h.js +7 -0
- writer/static/assets/BuilderApplicationSelect-XaM1D5fv.css +1 -0
- writer/static/assets/BuilderBlueprintLibraryPanel-Ckrhknlh.css +1 -0
- writer/static/assets/BuilderBlueprintLibraryPanel-DBDzhTlc.js +1 -0
- writer/static/assets/{BuilderEmbeddedCodeEditor-DiDqfWdt.css → BuilderEmbeddedCodeEditor-B0bcjlhk.css} +1 -1
- writer/static/assets/{BuilderEmbeddedCodeEditor-CbK-r9w6.js → BuilderEmbeddedCodeEditor-Dn7eDICN.js} +7 -7
- writer/static/assets/BuilderGraphSelect-C-LRsO8W.js +7 -0
- writer/static/assets/BuilderGraphSelect-D7B61d5s.css +1 -0
- writer/static/assets/{BuilderInsertionLabel-CDlWX-mE.js → BuilderInsertionLabel-BhyL9wgn.js} +1 -1
- writer/static/assets/{BuilderInsertionOverlay-B7-TsHNC.js → BuilderInsertionOverlay-MkAIVruY.js} +1 -1
- writer/static/assets/BuilderJournal-A0LcEwGI.js +7 -0
- writer/static/assets/BuilderJournal-DHv3Pvvm.css +1 -0
- writer/static/assets/BuilderModelSelect-CdSo_sih.js +7 -0
- writer/static/assets/BuilderModelSelect-Dc4IPLp2.css +1 -0
- writer/static/assets/BuilderSettings-BDwZBveu.js +16 -0
- writer/static/assets/BuilderSettings-lZkOXEYw.css +1 -0
- writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-3O6jKBXD.js +4 -0
- writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-DnX66iRg.css +1 -0
- writer/static/assets/BuilderSettingsDeploySharedBlueprint-BR_3ptsd.js +1 -0
- writer/static/assets/BuilderSettingsDeploySharedBlueprint-KJTl8gxP.css +1 -0
- writer/static/assets/BuilderSettingsHandlers-CBtEQFSo.js +1 -0
- writer/static/assets/BuilderSettingsHandlers-DJPeASfz.css +1 -0
- writer/static/assets/BuilderSidebarComponentTree-DLltgas5.js +1 -0
- writer/static/assets/BuilderSidebarComponentTree-DYu1F793.css +1 -0
- writer/static/assets/BuilderSidebarToolkit-CApZNTAq.js +7 -0
- writer/static/assets/BuilderSidebarToolkit-CwqbjRv8.css +1 -0
- writer/static/assets/BuilderTemplateEditor-CYSDeWgV.css +1 -0
- writer/static/assets/BuilderTemplateEditor-DnRDRcA0.js +87 -0
- writer/static/assets/BuilderVault-2vGoV0sx.js +1 -0
- writer/static/assets/BuilderVault-Cx6oQSES.css +1 -0
- writer/static/assets/ComponentRenderer-72hqvEvI.css +1 -0
- writer/static/assets/ComponentRenderer-D4Pj1i3s.js +1 -0
- writer/static/assets/SharedCopyClipboardButton-BipJKGtz.css +1 -0
- writer/static/assets/SharedCopyClipboardButton-DNI9kLe6.js +1 -0
- writer/static/assets/WdsCheckbox-DKvpPA4D.css +1 -0
- writer/static/assets/WdsCheckbox-edQcn1cf.js +1 -0
- writer/static/assets/WdsDropdownMenu-CzzPN9Wg.css +1 -0
- writer/static/assets/WdsDropdownMenu-DQnrRBNV.js +1 -0
- writer/static/assets/WdsFieldWrapper-Cmufx5Nj.js +1 -0
- writer/static/assets/WdsFieldWrapper-CsemOh8D.css +1 -0
- writer/static/assets/WdsTabs-DKj7BqI0.css +1 -0
- writer/static/assets/WdsTabs-DcfY_zn5.js +1 -0
- writer/static/assets/{art-paper-WsD9P5Lu.svg → art-paper-D70v1WMA.svg} +0 -1
- writer/static/assets/{cssMode-6B7VrieQ.js → cssMode-BYq4oZGq.js} +1 -1
- writer/static/assets/{freemarker2-Dy54TNCQ.js → freemarker2-CnNourkO.js} +1 -1
- writer/static/assets/{handlebars-BnWqX2x5.js → handlebars-Bm22yapJ.js} +1 -1
- writer/static/assets/{html-CIuj_eOg.js → html-CAKAfoZF.js} +1 -1
- writer/static/assets/{htmlMode-5fUQN2xJ.js → htmlMode-BGZ97n-V.js} +1 -1
- writer/static/assets/index-BKNuk68o.css +1 -0
- writer/static/assets/index-BQNXU3IR.js +17 -0
- writer/static/assets/index-DHXAd5Yn.js +4 -0
- writer/static/assets/index-Zki-pfO-.js +8525 -0
- writer/static/assets/index.esm-B1ZQtduY.js +17 -0
- writer/static/assets/{javascript-PLzaI1wY.js → javascript-X1f02eyK.js} +1 -1
- writer/static/assets/{jsonMode-CzZfZ4-D.js → jsonMode-hT0bNgT8.js} +1 -1
- writer/static/assets/{liquid-Cy21vWLb.js → liquid-KmCCiJw2.js} +1 -1
- writer/static/assets/{mapbox-gl-BjXsUCYi.js → mapbox-gl-C0cyFYYW.js} +1 -1
- writer/static/assets/{mdx-Cgik3q5p.js → mdx-DtRFauUw.js} +1 -1
- writer/static/assets/pdf-B6-yWJ-Y.js +12 -0
- writer/static/assets/pdf.worker.min-CyUfim15.mjs +21 -0
- writer/static/assets/{plotly.min-Dk-1ahEu.js → plotly.min-DutuuatZ.js} +1 -1
- writer/static/assets/{python-h6gjz_bN.js → python-DVhxg746.js} +1 -1
- writer/static/assets/{razor-BQ1k9241.js → razor-DR5Ns_BC.js} +1 -1
- writer/static/assets/{tsMode-BaBWt05D.js → tsMode-BNUEZzir.js} +1 -1
- writer/static/assets/{typescript-B42-gYGT.js → typescript-CRVt7Hx0.js} +1 -1
- writer/static/assets/useBlueprintRun-C00bCxh-.js +1 -0
- writer/static/assets/useKeyValueEditor-nDmI7cTJ.js +1 -0
- writer/static/assets/useListResources-DLkZhRSJ.js +1 -0
- writer/static/assets/{xml-BOez4Prd.js → xml-C_6-t1tb.js} +1 -1
- writer/static/assets/{yaml-BQhoEOIz.js → yaml-DIw8G7jk.js} +1 -1
- writer/static/components/annotatedtext.svg +3 -3
- writer/static/components/avatar.svg +3 -3
- writer/static/components/blueprints_addtostatelist.svg +3 -3
- writer/static/components/blueprints_apitrigger.svg +4 -0
- writer/static/components/blueprints_category_Logic.svg +3 -3
- writer/static/components/blueprints_category_Other.svg +3 -3
- writer/static/components/blueprints_category_Writer.svg +24 -5
- writer/static/components/blueprints_code.svg +6 -6
- writer/static/components/blueprints_crontrigger.svg +6 -0
- writer/static/components/blueprints_foreach.svg +3 -3
- writer/static/components/blueprints_httprequest.svg +6 -6
- writer/static/components/blueprints_logmessage.svg +10 -3
- writer/static/components/blueprints_parsejson.svg +3 -3
- writer/static/components/blueprints_returnvalue.svg +3 -3
- writer/static/components/blueprints_runblueprint.svg +3 -3
- writer/static/components/blueprints_setstate.svg +3 -3
- writer/static/components/blueprints_writeraddchatmessage.svg +14 -6
- writer/static/components/blueprints_writeraddtokg.svg +14 -6
- writer/static/components/blueprints_writerchatreply.svg +19 -0
- writer/static/components/blueprints_writerclassification.svg +23 -8
- writer/static/components/blueprints_writercompletion.svg +13 -5
- writer/static/components/blueprints_writernocodeapp.svg +13 -3
- writer/static/components/button.svg +3 -3
- writer/static/components/category_Content.svg +3 -3
- writer/static/components/category_Embed.svg +3 -3
- writer/static/components/category_Input.svg +4 -4
- writer/static/components/category_Layout.svg +6 -6
- writer/static/components/category_Other.svg +3 -3
- writer/static/components/chatbot.svg +3 -3
- writer/static/components/checkboxinput.svg +3 -3
- writer/static/components/colorinput.svg +6 -6
- writer/static/components/column.svg +3 -3
- writer/static/components/columns.svg +3 -3
- writer/static/components/dataframe.svg +3 -3
- writer/static/components/dateinput.svg +3 -3
- writer/static/components/dropdowninput.svg +4 -4
- writer/static/components/fileinput.svg +3 -3
- writer/static/components/googlemaps.svg +3 -3
- writer/static/components/heading.svg +6 -6
- writer/static/components/horizontalstack.svg +3 -3
- writer/static/components/html.svg +6 -6
- writer/static/components/icon.svg +3 -3
- writer/static/components/iframe.svg +3 -3
- writer/static/components/image.svg +10 -3
- writer/static/components/jsonviewer.svg +3 -3
- writer/static/components/link.svg +7 -7
- writer/static/components/mapbox.svg +3 -3
- writer/static/components/message.svg +3 -3
- writer/static/components/metric.svg +3 -3
- writer/static/components/multiselectinput.svg +3 -3
- writer/static/components/numberinput.svg +3 -3
- writer/static/components/pagination.svg +3 -3
- writer/static/components/pdf.svg +3 -3
- writer/static/components/plotlygraph.svg +6 -6
- writer/static/components/progressbar.svg +4 -4
- writer/static/components/radioinput.svg +3 -3
- writer/static/components/rangeinput.svg +3 -3
- writer/static/components/ratinginput.svg +3 -3
- writer/static/components/repeater.svg +3 -3
- writer/static/components/reuse.svg +3 -3
- writer/static/components/section.svg +3 -3
- writer/static/components/selectinput.svg +4 -4
- writer/static/components/separator.svg +3 -3
- writer/static/components/sidebar.svg +3 -3
- writer/static/components/sliderinput.svg +3 -3
- writer/static/components/step.svg +3 -3
- writer/static/components/steps.svg +3 -3
- writer/static/components/switchinput.svg +3 -3
- writer/static/components/tab.svg +3 -3
- writer/static/components/tabs.svg +3 -3
- writer/static/components/tags.svg +10 -3
- writer/static/components/text.svg +3 -3
- writer/static/components/textareainput.svg +10 -3
- writer/static/components/textinput.svg +3 -3
- writer/static/components/timeinput.svg +3 -3
- writer/static/components/timer.svg +3 -3
- writer/static/components/videoplayer.svg +10 -3
- writer/static/components/webcamcapture.svg +3 -3
- writer/static/index.html +3 -11
- writer/static/status/cancelled.svg +5 -0
- writer/static/status/skipped.svg +4 -0
- writer/static/status/stopped.svg +4 -0
- writer/sync.py +431 -0
- writer/ui.py +49 -41
- writer/vault.py +48 -0
- writer/wf_project.py +5 -5
- writer-1.25.1rc1.dist-info/METADATA +92 -0
- writer-1.25.1rc1.dist-info/RECORD +382 -0
- {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info}/WHEEL +1 -1
- writer/app_templates/default/.wf/components-blueprints_blueprint-0-t84xyhxau9ej3823.jsonl +0 -18
- writer/app_templates/default/static/welcome.svg +0 -40
- writer/static/assets/BaseMarkdown-BH_nSq9H.js +0 -1
- writer/static/assets/BlueprintToolbar-BO-WERxH.css +0 -1
- writer/static/assets/BlueprintToolbar-CHWL-rTm.js +0 -1
- writer/static/assets/BuilderApp-0XXiQ2l7.js +0 -7
- writer/static/assets/BuilderApp-CAhvLO4a.css +0 -1
- writer/static/assets/BuilderApplicationSelect-CwzU4F1-.js +0 -7
- writer/static/assets/BuilderApplicationSelect-DYYFtqjx.css +0 -1
- writer/static/assets/BuilderGraphSelect-CVO4gzts.css +0 -1
- writer/static/assets/BuilderGraphSelect-DV5Xy0HK.js +0 -7
- writer/static/assets/BuilderInstanceTracker-BECcXNnW.css +0 -1
- writer/static/assets/BuilderInstanceTracker-Dr0XhpSH.js +0 -1
- writer/static/assets/BuilderModelSelect-QHUGd86u.css +0 -1
- writer/static/assets/BuilderModelSelect-ow0XEf2t.js +0 -7
- writer/static/assets/BuilderSettings-C2WRfSor.css +0 -1
- writer/static/assets/BuilderSettings-D5bjbcWj.js +0 -24
- writer/static/assets/BuilderSettingsHandlers-1QLaHR8J.css +0 -1
- writer/static/assets/BuilderSettingsHandlers-j1aMAV4J.js +0 -1
- writer/static/assets/BuilderSidebarComponentTree-0YajaJke.css +0 -1
- writer/static/assets/BuilderSidebarComponentTree-CtaOZfpV.js +0 -1
- writer/static/assets/BuilderSidebarPanel-BMJVzhd3.js +0 -1
- writer/static/assets/BuilderSidebarPanel-BrLsNxVM.css +0 -1
- writer/static/assets/BuilderSidebarToolkit-BbjOOp8E.js +0 -1
- writer/static/assets/BuilderSidebarToolkit-BvZDShKD.css +0 -1
- writer/static/assets/ComponentRenderer-B76bKRZO.css +0 -1
- writer/static/assets/ComponentRenderer-CZs4z773.js +0 -1
- writer/static/assets/SharedMoreDropdown-BWKlox8E.css +0 -1
- writer/static/assets/SharedMoreDropdown-DKv_HNef.js +0 -7
- writer/static/assets/WdsDropdownMenu-C1UyKOJR.css +0 -1
- writer/static/assets/WdsDropdownMenu-CGiATY2E.js +0 -1
- writer/static/assets/WdsLoaderDots-qdyk2N-2.js +0 -1
- writer/static/assets/index-BJMAe9SN.js +0 -8
- writer/static/assets/index-CPCeQU9V.css +0 -1
- writer/static/assets/index-ChWW_c_j.js +0 -439
- writer/static/assets/instancePath-BsbOTTI8.js +0 -1
- writer/static/assets/material-symbols-outlined-latin-wght-normal-DuE-q1Ez.woff2 +0 -0
- writer/static/assets/useBlueprintRun-CBOvzWTA.js +0 -1
- writer/static/assets/useComponentDescription-FHKxu8gg.js +0 -1
- writer/static/assets/useListResources-BpMgq7XI.js +0 -1
- writer-0.8.3rc21.dist-info/METADATA +0 -117
- writer-0.8.3rc21.dist-info/RECORD +0 -342
- {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info}/entry_points.txt +0 -0
- {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info/licenses}/LICENSE.txt +0 -0
writer/app_runner.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import concurrent.futures
|
|
3
3
|
import importlib.util
|
|
4
|
+
import io
|
|
4
5
|
import logging
|
|
5
6
|
import logging.handlers
|
|
6
7
|
import multiprocessing
|
|
@@ -11,7 +12,9 @@ import shutil
|
|
|
11
12
|
import signal
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
15
|
+
import tempfile
|
|
14
16
|
import threading
|
|
17
|
+
import zipfile
|
|
15
18
|
from types import ModuleType
|
|
16
19
|
from typing import Any, Callable, Dict, List, Optional, Union, cast
|
|
17
20
|
|
|
@@ -19,7 +22,7 @@ import watchdog.events
|
|
|
19
22
|
from pydantic import ValidationError
|
|
20
23
|
from watchdog.observers.polling import PollingObserver
|
|
21
24
|
|
|
22
|
-
from writer import VERSION, audit_and_fix, core_ui, crypto, wf_project
|
|
25
|
+
from writer import VERSION, audit_and_fix, core_ui, crypto, vault, wf_project
|
|
23
26
|
from writer.core import (
|
|
24
27
|
Config,
|
|
25
28
|
EventHandlerRegistry,
|
|
@@ -28,6 +31,7 @@ from writer.core import (
|
|
|
28
31
|
use_request_context,
|
|
29
32
|
)
|
|
30
33
|
from writer.core_ui import ingest_bmc_component_tree
|
|
34
|
+
from writer.logs import use_logging_redirect, use_stdout_redirect
|
|
31
35
|
from writer.ss_types import (
|
|
32
36
|
AppProcessServerRequest,
|
|
33
37
|
AppProcessServerRequestPacket,
|
|
@@ -46,6 +50,7 @@ from writer.ss_types import (
|
|
|
46
50
|
InitSessionResponsePayload,
|
|
47
51
|
ListResourcesRequest,
|
|
48
52
|
ListResourcesRequestPayload,
|
|
53
|
+
QueueMessageRequest,
|
|
49
54
|
ServeMode,
|
|
50
55
|
SourceFilesDirectory,
|
|
51
56
|
StateContentRequest,
|
|
@@ -54,10 +59,11 @@ from writer.ss_types import (
|
|
|
54
59
|
StateEnquiryResponsePayload,
|
|
55
60
|
WriterApplicationInformation,
|
|
56
61
|
WriterEvent,
|
|
62
|
+
WriterVaultUpdateRequest,
|
|
57
63
|
)
|
|
58
64
|
from writer.wf_project import WfProjectContext
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
user_code_logger = logging.getLogger("user_code")
|
|
61
67
|
|
|
62
68
|
|
|
63
69
|
class MessageHandlingException(Exception):
|
|
@@ -172,13 +178,19 @@ class AppProcess(multiprocessing.Process):
|
|
|
172
178
|
session.session_component_tree, mode=writer.Config.mode
|
|
173
179
|
)
|
|
174
180
|
|
|
181
|
+
headers = session.headers or {}
|
|
175
182
|
writer_application: Optional[WriterApplicationInformation] = None
|
|
176
|
-
writer_app_id = os.getenv("WRITER_APP_ID")
|
|
177
|
-
writer_org_id = os.getenv("WRITER_ORG_ID")
|
|
183
|
+
writer_app_id = headers.get("x-agent-id") or os.getenv("WRITER_APP_ID")
|
|
184
|
+
writer_org_id = headers.get("x-organization-id") or os.getenv("WRITER_ORG_ID")
|
|
185
|
+
writer_base_url = os.getenv("WRITER_BASE_URL", "https://api.writer.com")
|
|
178
186
|
if writer_app_id is not None and writer_org_id is not None:
|
|
179
187
|
writer_application = WriterApplicationInformation(
|
|
180
|
-
id=writer_app_id,
|
|
188
|
+
id=writer_app_id,
|
|
189
|
+
organizationId=writer_org_id,
|
|
190
|
+
baseUrl=writer_base_url
|
|
181
191
|
)
|
|
192
|
+
if writer.Config.mode == "edit":
|
|
193
|
+
writer_application.apiKey = os.getenv("WRITER_API_KEY")
|
|
182
194
|
|
|
183
195
|
res_payload = InitSessionResponsePayload(
|
|
184
196
|
userState=user_state,
|
|
@@ -315,6 +327,7 @@ class AppProcess(multiprocessing.Process):
|
|
|
315
327
|
"type": app.type,
|
|
316
328
|
"status": app.status,
|
|
317
329
|
"organization_id": organization_id,
|
|
330
|
+
"inputs": {i.name: "" for i in app.inputs},
|
|
318
331
|
}
|
|
319
332
|
for app in applications
|
|
320
333
|
]
|
|
@@ -402,6 +415,17 @@ class AppProcess(multiprocessing.Process):
|
|
|
402
415
|
session.userinfo = request.payload
|
|
403
416
|
return AppProcessServerResponse(status="ok", status_message=None, payload=None)
|
|
404
417
|
|
|
418
|
+
if type == "queueMessage":
|
|
419
|
+
session.queued_messages.append(request.payload)
|
|
420
|
+
return AppProcessServerResponse(status="ok", status_message=None, payload=None)
|
|
421
|
+
|
|
422
|
+
if type == "retrieveMessages":
|
|
423
|
+
return AppProcessServerResponse(status="ok", status_message=None, payload=session.queued_messages)
|
|
424
|
+
|
|
425
|
+
if type == "clearMessages":
|
|
426
|
+
session.queued_messages = []
|
|
427
|
+
return AppProcessServerResponse(status="ok", status_message=None, payload=None)
|
|
428
|
+
|
|
405
429
|
if self.mode == "edit" and type == "hashRequest":
|
|
406
430
|
hash_request_payload = HashRequestPayload.model_validate(request.payload)
|
|
407
431
|
return AppProcessServerResponse(
|
|
@@ -419,6 +443,14 @@ class AppProcess(multiprocessing.Process):
|
|
|
419
443
|
list_req_payload = ListResourcesRequestPayload.model_validate(request.payload)
|
|
420
444
|
return self._handle_list_resources(session, list_req_payload)
|
|
421
445
|
|
|
446
|
+
if self.mode == "edit" and type == "writerVaultUpdate":
|
|
447
|
+
vault.writer_vault.refresh()
|
|
448
|
+
return AppProcessServerResponse(
|
|
449
|
+
status="ok",
|
|
450
|
+
status_message=None,
|
|
451
|
+
payload=None,
|
|
452
|
+
)
|
|
453
|
+
|
|
422
454
|
raise MessageHandlingException("Invalid event.")
|
|
423
455
|
|
|
424
456
|
def _execute_user_code(self) -> None:
|
|
@@ -427,7 +459,6 @@ class AppProcess(multiprocessing.Process):
|
|
|
427
459
|
"""
|
|
428
460
|
|
|
429
461
|
import io
|
|
430
|
-
from contextlib import redirect_stdout
|
|
431
462
|
|
|
432
463
|
import writer
|
|
433
464
|
|
|
@@ -436,19 +467,65 @@ class AppProcess(multiprocessing.Process):
|
|
|
436
467
|
raise ValueError("Couldn't find app module (writeruserapp).")
|
|
437
468
|
|
|
438
469
|
code_path = os.path.join(self.app_path, "main.py")
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
470
|
+
|
|
471
|
+
# Containers to capture logs for KV storage
|
|
472
|
+
init_stdout_container = ['']
|
|
473
|
+
init_logs_container = ['']
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
with (
|
|
477
|
+
use_stdout_redirect([
|
|
478
|
+
lambda entry: writer.core.initial_state.add_log_entry("info", "Stdout message during initialization", entry),
|
|
479
|
+
lambda entry: init_stdout_container.__setitem__(0, entry)
|
|
480
|
+
]),
|
|
481
|
+
use_logging_redirect([
|
|
482
|
+
lambda entry: writer.core.initial_state.add_log_entry("info", "Logs during initialization", entry),
|
|
483
|
+
lambda entry: init_logs_container.__setitem__(0, entry)
|
|
484
|
+
]),
|
|
485
|
+
):
|
|
486
|
+
writeruserapp.__dict__["logger"] = user_code_logger
|
|
487
|
+
code = compile(self.run_code, code_path, "exec")
|
|
488
|
+
exec(code, writeruserapp.__dict__)
|
|
489
|
+
finally:
|
|
490
|
+
self._save_initialization_logs(init_stdout_container[0], init_logs_container[0])
|
|
448
491
|
|
|
449
492
|
# Register non-private functions as handlers
|
|
450
493
|
self.handler_registry.register_module(writeruserapp)
|
|
451
494
|
|
|
495
|
+
def _save_initialization_logs(self, stdout: str, logs: str) -> None:
|
|
496
|
+
"""Save main.py initialization logs to KV storage."""
|
|
497
|
+
if not stdout and not logs:
|
|
498
|
+
return
|
|
499
|
+
|
|
500
|
+
from datetime import datetime, timezone
|
|
501
|
+
|
|
502
|
+
from writer.core import Config
|
|
503
|
+
from writer.journal import INIT_LOGS_KEY_PREFIX
|
|
504
|
+
from writer.keyvalue_storage import writer_kv_storage
|
|
505
|
+
|
|
506
|
+
if "journal" not in Config.feature_flags or not writer_kv_storage.is_accessible():
|
|
507
|
+
return
|
|
508
|
+
|
|
509
|
+
timestamp = datetime.now(timezone.utc)
|
|
510
|
+
# Match JournalRecord.instance_type logic: 'e' for editor, 'a' for agent
|
|
511
|
+
instance_type = "editor" if self.mode == "edit" else "agent"
|
|
512
|
+
instance_type_letter = instance_type[0] # 'e' or 'a'
|
|
513
|
+
|
|
514
|
+
key = f"{INIT_LOGS_KEY_PREFIX}{instance_type_letter}-{int(timestamp.timestamp() * 1000)}"
|
|
515
|
+
data = {
|
|
516
|
+
"timestamp": timestamp.isoformat(),
|
|
517
|
+
"instanceType": instance_type,
|
|
518
|
+
"mode": self.mode,
|
|
519
|
+
"stdout": stdout,
|
|
520
|
+
"logs": logs
|
|
521
|
+
}
|
|
522
|
+
try:
|
|
523
|
+
writer_kv_storage.save(key, data)
|
|
524
|
+
except Exception as e:
|
|
525
|
+
# Don't fail initialization if log saving fails
|
|
526
|
+
app_logger = logging.getLogger("app_runner")
|
|
527
|
+
app_logger.warning(f"Failed to save initialization logs to KV storage: {e}")
|
|
528
|
+
|
|
452
529
|
def _apply_configuration(self) -> None:
|
|
453
530
|
import writer
|
|
454
531
|
|
|
@@ -561,7 +638,7 @@ class AppProcess(multiprocessing.Process):
|
|
|
561
638
|
pass
|
|
562
639
|
|
|
563
640
|
self.is_app_process_server_ready.set()
|
|
564
|
-
while True: # Starts app message server
|
|
641
|
+
while True and not is_app_process_server_terminated.is_set(): # Starts app message server
|
|
565
642
|
try:
|
|
566
643
|
if not self.server_conn.poll(1):
|
|
567
644
|
continue
|
|
@@ -571,10 +648,7 @@ class AppProcess(multiprocessing.Process):
|
|
|
571
648
|
terminate_server()
|
|
572
649
|
return
|
|
573
650
|
self._handle_app_process_server_packet(packet)
|
|
574
|
-
except
|
|
575
|
-
terminate_server()
|
|
576
|
-
return
|
|
577
|
-
except BaseException as e:
|
|
651
|
+
except Exception as e:
|
|
578
652
|
self.logger.error(f"Unexpected exception in AppProcess server.\n{repr(e)}")
|
|
579
653
|
terminate_server()
|
|
580
654
|
return
|
|
@@ -589,8 +663,9 @@ class AppProcess(multiprocessing.Process):
|
|
|
589
663
|
thread_pool_future.add_done_callback(self._send_packet)
|
|
590
664
|
|
|
591
665
|
def run(self) -> None:
|
|
666
|
+
max_workers = int(os.getenv("WRITER_MAX_WORKERS", (os.cpu_count() or 4) * 10))
|
|
592
667
|
self.executor = concurrent.futures.ThreadPoolExecutor(
|
|
593
|
-
max_workers=
|
|
668
|
+
max_workers=max_workers,
|
|
594
669
|
)
|
|
595
670
|
self.server_conn_lock = threading.Lock()
|
|
596
671
|
self.client_conn.close()
|
|
@@ -680,8 +755,6 @@ class LogListener(threading.Thread):
|
|
|
680
755
|
super().__init__(name="LogListenerThread")
|
|
681
756
|
self.log_queue = log_queue
|
|
682
757
|
self.logger = logging.getLogger("from_app")
|
|
683
|
-
self.logger.setLevel(logging.INFO)
|
|
684
|
-
self.logger.addHandler(logging.StreamHandler())
|
|
685
758
|
|
|
686
759
|
def run(self) -> None:
|
|
687
760
|
while True:
|
|
@@ -738,23 +811,26 @@ class AppRunner:
|
|
|
738
811
|
self.serve_loop = asyncio.get_running_loop()
|
|
739
812
|
|
|
740
813
|
def _set_logger(self):
|
|
741
|
-
logger = logging.getLogger("
|
|
814
|
+
logger = logging.getLogger("app_runner")
|
|
742
815
|
logger.addHandler(logging.handlers.QueueHandler(self.log_queue))
|
|
743
816
|
self.log_listener = LogListener(self.log_queue)
|
|
744
817
|
self.log_listener.start()
|
|
745
818
|
|
|
746
819
|
def _start_fs_observer(self):
|
|
747
|
-
self.observer
|
|
820
|
+
if self.observer is None:
|
|
821
|
+
self.observer = PollingObserver(AppRunner.UPDATE_CHECK_INTERVAL_SECONDS)
|
|
748
822
|
self.observer.schedule(
|
|
749
823
|
FileEventHandler(self.reload_code_from_saved, patterns=["*.py"]),
|
|
750
824
|
path=self.app_path,
|
|
751
825
|
recursive=True,
|
|
752
826
|
)
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
827
|
+
# See _install_requirements docstring for info
|
|
828
|
+
# self.observer.schedule(
|
|
829
|
+
# FileEventHandler(self._install_requirements, patterns=["requirements.txt"]),
|
|
830
|
+
# path=self.app_path,
|
|
831
|
+
# )
|
|
832
|
+
if not self.observer.is_alive():
|
|
833
|
+
self.observer.start()
|
|
758
834
|
|
|
759
835
|
def _start_wf_project_process_write_files(self):
|
|
760
836
|
wf_project.start_process_write_files_async(
|
|
@@ -762,6 +838,11 @@ class AppRunner:
|
|
|
762
838
|
)
|
|
763
839
|
|
|
764
840
|
def _install_requirements(self) -> None:
|
|
841
|
+
"""
|
|
842
|
+
Not used anywhere anymore as this method of installing dependencies is not supported.
|
|
843
|
+
Left because might change in the future.
|
|
844
|
+
"""
|
|
845
|
+
|
|
765
846
|
logger = logging.getLogger("writer")
|
|
766
847
|
logger.debug("\nDetected changes in requirements.txt. Installing dependencies...")
|
|
767
848
|
try:
|
|
@@ -918,8 +999,13 @@ class AppRunner:
|
|
|
918
999
|
raise error
|
|
919
1000
|
|
|
920
1001
|
def _check_file_in_app_path(self, path):
|
|
921
|
-
|
|
922
|
-
|
|
1002
|
+
app_path = os.path.abspath(self.app_path)
|
|
1003
|
+
file_path = os.path.abspath(path)
|
|
1004
|
+
if file_path == app_path or not file_path.startswith(app_path):
|
|
1005
|
+
raise PermissionError(f"{path} should be inside of application ({self.app_path})")
|
|
1006
|
+
wf_path = os.path.abspath(os.path.join(self.app_path, ".wf"))
|
|
1007
|
+
if file_path.startswith(wf_path):
|
|
1008
|
+
raise PermissionError(f"{path} should not be inside of Writer Framework files ({wf_path})")
|
|
923
1009
|
|
|
924
1010
|
def _load_persisted_components(self) -> Dict[str, ComponentDefinition]:
|
|
925
1011
|
logger = logging.getLogger("writer")
|
|
@@ -937,6 +1023,23 @@ class AppRunner:
|
|
|
937
1023
|
components = audit_and_fix.fix_components(components)
|
|
938
1024
|
return components
|
|
939
1025
|
|
|
1026
|
+
async def queue_message(self, session_id: str, data: Any) -> AppProcessServerResponse:
|
|
1027
|
+
return await self.dispatch_message(session_id, QueueMessageRequest(type="queueMessage", payload=data))
|
|
1028
|
+
|
|
1029
|
+
async def retrieve_messages(self, session_id: str) -> list:
|
|
1030
|
+
response = await self.dispatch_message(
|
|
1031
|
+
session_id, AppProcessServerRequest(type="retrieveMessages", payload=None)
|
|
1032
|
+
)
|
|
1033
|
+
if isinstance(response.payload, list):
|
|
1034
|
+
return response.payload
|
|
1035
|
+
return []
|
|
1036
|
+
|
|
1037
|
+
async def clear_messages(self, session_id: str) -> AppProcessServerResponse:
|
|
1038
|
+
response = await self.dispatch_message(
|
|
1039
|
+
session_id, AppProcessServerRequest(type="clearMessages", payload=None)
|
|
1040
|
+
)
|
|
1041
|
+
return response
|
|
1042
|
+
|
|
940
1043
|
async def check_session(self, session_id: str) -> bool:
|
|
941
1044
|
response = await self.dispatch_message(
|
|
942
1045
|
session_id, AppProcessServerRequest(type="checkSession", payload=None)
|
|
@@ -973,6 +1076,10 @@ class AppRunner:
|
|
|
973
1076
|
message = ListResourcesRequest(type="listResources", payload=message_payload)
|
|
974
1077
|
return await self.dispatch_message(session_id, message)
|
|
975
1078
|
|
|
1079
|
+
async def writer_vault_refresh(self, session_id: str) -> AppProcessServerResponse:
|
|
1080
|
+
message = WriterVaultUpdateRequest(type="writerVaultUpdate")
|
|
1081
|
+
return await self.dispatch_message(session_id, message)
|
|
1082
|
+
|
|
976
1083
|
async def handle_event(self, session_id: str, event: WriterEvent) -> AppProcessServerResponse:
|
|
977
1084
|
return await self.dispatch_message(session_id, EventRequest(type="event", payload=event))
|
|
978
1085
|
|
|
@@ -1011,6 +1118,103 @@ class AppRunner:
|
|
|
1011
1118
|
|
|
1012
1119
|
self.source_files = wf_project.build_source_files(self.app_path)
|
|
1013
1120
|
|
|
1121
|
+
def export_zip(self):
|
|
1122
|
+
if self.mode != "edit":
|
|
1123
|
+
raise PermissionError("Cannot export in non-edit mode.")
|
|
1124
|
+
zip_buffer = io.BytesIO()
|
|
1125
|
+
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
1126
|
+
for root, dirs, files in os.walk(self.app_path):
|
|
1127
|
+
for file in files:
|
|
1128
|
+
if file.endswith('.pyc'):
|
|
1129
|
+
continue
|
|
1130
|
+
full_path = os.path.join(root, file)
|
|
1131
|
+
arcname = os.path.relpath(full_path, start=self.app_path)
|
|
1132
|
+
zipf.write(full_path, arcname=arcname)
|
|
1133
|
+
zip_buffer.seek(0)
|
|
1134
|
+
return zip_buffer
|
|
1135
|
+
|
|
1136
|
+
def _sync_folders(self, src: str, dst: str):
|
|
1137
|
+
"""
|
|
1138
|
+
Synchronizes the contents of the source folder to the destination folder in a one-way manner.
|
|
1139
|
+
|
|
1140
|
+
- Copies all files and subdirectories from `src` to `dst`.
|
|
1141
|
+
- Creates any missing directories in `dst` to match `src`.
|
|
1142
|
+
- Removes any files or directories in `dst` that do not exist in `src`.
|
|
1143
|
+
"""
|
|
1144
|
+
# Create dst if it doesn't exist
|
|
1145
|
+
os.makedirs(dst, exist_ok=True)
|
|
1146
|
+
|
|
1147
|
+
# Copy files and folders from src to dst
|
|
1148
|
+
for root, dirs, files in os.walk(src):
|
|
1149
|
+
rel_path = os.path.relpath(root, src)
|
|
1150
|
+
dst_path = os.path.join(dst, rel_path)
|
|
1151
|
+
|
|
1152
|
+
# Create directories in dst
|
|
1153
|
+
os.makedirs(dst_path, exist_ok=True)
|
|
1154
|
+
|
|
1155
|
+
# Copy files
|
|
1156
|
+
for file in files:
|
|
1157
|
+
src_file = os.path.join(root, file)
|
|
1158
|
+
dst_file = os.path.join(dst_path, file)
|
|
1159
|
+
shutil.copy2(src_file, dst_file)
|
|
1160
|
+
|
|
1161
|
+
# Remove files and folders in dst that don't exist in src
|
|
1162
|
+
for root, dirs, files in os.walk(dst):
|
|
1163
|
+
rel_path = os.path.relpath(root, dst)
|
|
1164
|
+
src_path = os.path.join(src, rel_path)
|
|
1165
|
+
|
|
1166
|
+
# Remove files not in src
|
|
1167
|
+
for file in files:
|
|
1168
|
+
dst_file = os.path.join(root, file)
|
|
1169
|
+
src_file = os.path.join(src_path, file)
|
|
1170
|
+
if not os.path.exists(src_file):
|
|
1171
|
+
os.remove(dst_file)
|
|
1172
|
+
|
|
1173
|
+
# Remove empty directories not in src
|
|
1174
|
+
for dir in dirs:
|
|
1175
|
+
dst_dir = os.path.join(root, dir)
|
|
1176
|
+
src_dir = os.path.join(src_path, dir)
|
|
1177
|
+
if not os.path.exists(src_dir):
|
|
1178
|
+
shutil.rmtree(dst_dir)
|
|
1179
|
+
|
|
1180
|
+
async def import_zip(self, zip_path: str):
|
|
1181
|
+
if self.mode != "edit":
|
|
1182
|
+
raise PermissionError("Cannot import in non-edit mode.")
|
|
1183
|
+
|
|
1184
|
+
try:
|
|
1185
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
1186
|
+
extracted_path = os.path.join(tmpdir, "imported_agent")
|
|
1187
|
+
os.makedirs(extracted_path, exist_ok=True)
|
|
1188
|
+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
1189
|
+
zip_ref.extractall(extracted_path)
|
|
1190
|
+
|
|
1191
|
+
main_py_dir = None
|
|
1192
|
+
for root, _, files in os.walk(extracted_path):
|
|
1193
|
+
if "main.py" in files:
|
|
1194
|
+
main_py_dir = root
|
|
1195
|
+
break
|
|
1196
|
+
|
|
1197
|
+
if main_py_dir is None:
|
|
1198
|
+
raise ValueError("main.py not found in the imported archive.")
|
|
1199
|
+
|
|
1200
|
+
wf_dir_path = os.path.join(main_py_dir, ".wf")
|
|
1201
|
+
if not os.path.isdir(wf_dir_path):
|
|
1202
|
+
raise ValueError(".wf directory not found alongside main.py in the archive.")
|
|
1203
|
+
|
|
1204
|
+
# Passed all checks; replace current app contents
|
|
1205
|
+
|
|
1206
|
+
logging.info("Copying app at %s", main_py_dir)
|
|
1207
|
+
if self.observer is not None:
|
|
1208
|
+
self.observer.unschedule_all()
|
|
1209
|
+
|
|
1210
|
+
self._sync_folders(main_py_dir, self.app_path)
|
|
1211
|
+
|
|
1212
|
+
self._start_fs_observer()
|
|
1213
|
+
self.bmc_components = self._load_persisted_components()
|
|
1214
|
+
self.reload_code_from_saved()
|
|
1215
|
+
except zipfile.BadZipFile:
|
|
1216
|
+
raise ValueError("Uploaded file is not a valid ZIP.")
|
|
1217
|
+
|
|
1014
1218
|
def _clean_process(self) -> None:
|
|
1015
1219
|
# Terminate the AppProcess server by sending an empty message
|
|
1016
1220
|
# The empty message will bounce an empty message and terminate the client too
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{"id": "0decp3w5erhvl0nw", "type": "blueprints_blueprint", "content": {"key": "button@click_1"}, "handlers": {}, "isCodeManaged": false, "parentId": "blueprints_root", "position": 0}
|
|
2
|
+
{"id": "dv1jzdoaz27j60wq", "type": "blueprints_uieventtrigger", "content": {"alias": "Draft response clicked", "refComponentId": "zazp9q0cpsglynsb", "refEventType": "wf-click"}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "nxwa9ls502oqhy9u", "outId": "trigger"}], "parentId": "0decp3w5erhvl0nw", "position": 0, "x": 72, "y": 624}
|
|
3
|
+
{"id": "nxwa9ls502oqhy9u", "type": "blueprints_setstate", "content": {"alias": "Set progress message", "element": "progress_message", "value": "% Reviewing..."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "r0314ycb8wb21pc8", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 1, "x": 456, "y": 576}
|
|
4
|
+
{"id": "r0314ycb8wb21pc8", "type": "blueprints_writerclassification", "content": {"alias": "Classify review category", "categories": "{\n \"Packaging\": \"The review mentions packaging issues or compliments.\",\n \"Pricing\": \"The review discusses pricing concerns or satisfaction.\",\n \"Quality\": \"The review is about the quality of the product or service.\",\n \"Delivery\": \"The review relates to delivery times or issues.\",\n \"Empty\": \"A review is mentioned but the review cannot be found\"\n}", "text": "The review ---- @{customer_review}"}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "fmltu4qrq9qnu7vk", "outId": "category_Packaging"}, {"toNodeId": "n63w3bvyin80gus4", "outId": "category_Pricing"}, {"toNodeId": "uolelullwetqhgbv", "outId": "category_Quality"}, {"toNodeId": "fyvf1qtrpq85vac3", "outId": "category_Delivery"}, {"toNodeId": "2goghkw7qirzs9u9", "outId": "category_Empty"}], "parentId": "0decp3w5erhvl0nw", "position": 2, "x": 840, "y": 504}
|
|
5
|
+
{"id": "fmltu4qrq9qnu7vk", "type": "blueprints_writercompletion", "content": {"alias": "Draft packaging response", "modelId": "palmyra-x5", "prompt": "Take the role of a customer success rep and draft an email response to the customer review below that mentions packaging: @{customer_review}\n\nThe response should be short, positive and helpful in style.\n\nAcknowledge their feedback about the packaging. Express appreciation if it\u2019s positive, or apologize and offer a resolution if it\u2019s negative. Mention steps we're taking to improve or maintain packaging standards."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "253p2vp9msfuywdo", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 3, "x": 1224, "y": 96}
|
|
6
|
+
{"id": "n63w3bvyin80gus4", "type": "blueprints_writercompletion", "content": {"alias": "Draft pricing response", "modelId": "palmyra-x5", "prompt": "Take the role of a customer success rep and draft an email response to the customer review below that mentions pricing: @{customer_review}\n\nRespond to the customer\u2019s comments on pricing. If it\u2019s positive, thank them for recognizing the value. If it\u2019s negative, empathize and briefly explain our pricing approach or any ongoing offers that may help."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "253p2vp9msfuywdo", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 4, "x": 1224, "y": 360}
|
|
7
|
+
{"id": "uolelullwetqhgbv", "type": "blueprints_writercompletion", "content": {"alias": "Draft quality response", "modelId": "palmyra-x5", "prompt": "Take the role of a customer success rep and draft an email response to the customer review below that mentions quality: @{customer_review}\n\nCraft a response focused on product quality. Thank the reviewer if they\u2019re satisfied. If not, apologize sincerely and offer help or a replacement, while reinforcing our commitment to high standards."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "253p2vp9msfuywdo", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 5, "x": 1224, "y": 576}
|
|
8
|
+
{"id": "fyvf1qtrpq85vac3", "type": "blueprints_writercompletion", "content": {"alias": "Draft delivery response", "modelId": "palmyra-x5", "prompt": "Take the role of a customer success rep and draft an email response to the customer review below that mentions delivery: @{customer_review}\n\nAddress the customer\u2019s delivery experience. Thank them if it was smooth, or apologize and investigate if it was delayed or problematic. Reassure them we\u2019re improving logistics and care about their satisfaction."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "253p2vp9msfuywdo", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 6, "x": 1224, "y": 816}
|
|
9
|
+
{"id": "2goghkw7qirzs9u9", "type": "blueprints_setstate", "content": {"alias": "Handle empty review", "element": "review_response", "value": "You must specify a review."}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "253p2vp9msfuywdo", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 7, "x": 1224, "y": 1056}
|
|
10
|
+
{"id": "253p2vp9msfuywdo", "type": "blueprints_setstate", "content": {"alias": "Store response", "element": "review_response", "value": "@{result}"}, "handlers": {}, "isCodeManaged": false, "outs": [{"toNodeId": "x4flvjxp3vcyz21s", "outId": "success"}], "parentId": "0decp3w5erhvl0nw", "position": 8, "x": 1608, "y": 576}
|
|
11
|
+
{"id": "x4flvjxp3vcyz21s", "type": "blueprints_setstate", "content": {"alias": "Clear progress message", "element": "progress_message"}, "handlers": {}, "isCodeManaged": false, "parentId": "0decp3w5erhvl0nw", "position": 9, "x": 1992, "y": 576}
|
writer/app_templates/default/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
{"id": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "type": "page", "content": {"pageMode": "compact"}, "handlers": {}, "isCodeManaged": false, "parentId": "root", "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
2
|
-
{"id": "
|
|
3
|
-
{"id": "
|
|
4
|
-
{"id": "
|
|
5
|
-
{"id": "
|
|
6
|
-
{"id": "
|
|
7
|
-
{"id": "
|
|
8
|
-
{"id": "
|
|
9
|
-
{"id": "
|
|
2
|
+
{"id": "2ime7p1jioyr1yz2", "type": "sidebar", "content": {"sidebarBackgroundColor": "#ECE1FF"}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": -2, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
3
|
+
{"id": "3toyuvfwhlwsq8e6", "type": "tags", "content": {"primaryTextColor": "#ffffff", "referenceColor": "#4A46DA", "rotateHue": "no", "tags": "{\n \"Public Beta\": \"Public Beta\"\n}"}, "handlers": {}, "isCodeManaged": false, "parentId": "2ime7p1jioyr1yz2", "position": 0}
|
|
4
|
+
{"id": "pb3ooumyznopaqh4", "type": "heading", "content": {"headingType": "h1", "text": "Welcome to Agent Builder"}, "handlers": {}, "isCodeManaged": false, "parentId": "2ime7p1jioyr1yz2", "position": 1}
|
|
5
|
+
{"id": "a4rbvwms5exs3bta", "type": "text", "content": {"text": "A collaborative platform for engineers and business users to design, build and deploy AI agents together."}, "handlers": {}, "isCodeManaged": false, "parentId": "2ime7p1jioyr1yz2", "position": 2}
|
|
6
|
+
{"id": "y2mrnml4ukza7uwd", "type": "image", "content": {"caption": "", "maxWidth": "160px", "src": "static/agent_builder_demo.png"}, "handlers": {}, "isCodeManaged": false, "parentId": "2ime7p1jioyr1yz2", "position": 3}
|
|
7
|
+
{"id": "29qur7xljy4faoqt", "type": "section", "content": {"title": ""}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 0}
|
|
8
|
+
{"id": "7fz70dyp64raqr7u", "type": "heading", "content": {"headingType": "h1", "text": "Example Customer Support Agent "}, "handlers": {}, "isCodeManaged": false, "parentId": "29qur7xljy4faoqt", "position": 0}
|
|
9
|
+
{"id": "fkxnuw7eitb422j6", "type": "text", "content": {"text": "Test this agent in Preview and then go to Interface and Blueprints to see how it was built. ", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "29qur7xljy4faoqt", "position": 1}
|
|
10
|
+
{"id": "5dgdzkdrgc5a9yss", "type": "separator", "content": {}, "handlers": {}, "isCodeManaged": false, "parentId": "29qur7xljy4faoqt", "position": 2}
|
|
11
|
+
{"id": "iee6l0lcbz2cne8q", "type": "columns", "content": {}, "handlers": {}, "isCodeManaged": false, "parentId": "29qur7xljy4faoqt", "position": 3}
|
|
12
|
+
{"id": "cqkp4hx6rjurlrhd", "type": "column", "content": {"width": "1"}, "handlers": {}, "isCodeManaged": false, "parentId": "iee6l0lcbz2cne8q", "position": 0}
|
|
13
|
+
{"id": "2mybry5io5mmww7x", "type": "section", "content": {"containerBackgroundColor": "#fcfcfc", "title": ""}, "handlers": {}, "isCodeManaged": false, "parentId": "cqkp4hx6rjurlrhd", "position": 0}
|
|
14
|
+
{"id": "f52d0p6b65rjqkpx", "type": "heading", "content": {"headingType": "h3", "text": "1. Copy Review"}, "handlers": {}, "isCodeManaged": false, "parentId": "2mybry5io5mmww7x", "position": 0}
|
|
15
|
+
{"id": "el6v2mozugelu9mw", "type": "text", "content": {"primaryTextColor": "", "text": "*I\u2019ve been searching for the perfect tailored blazer for years and I think I\u2019ve finally found it. The cut is precise without feeling restrictive. The material is rich and smooth. It instantly elevates any outfit and makes me feel confident the moment I put it on. I've already received several compliments after just one wear.*", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "2mybry5io5mmww7x", "position": 1}
|
|
16
|
+
{"id": "gzl9y3qtexw7vmwg", "type": "column", "content": {"width": "1"}, "handlers": {}, "isCodeManaged": false, "parentId": "iee6l0lcbz2cne8q", "position": 1}
|
|
17
|
+
{"id": "p7golp2r05uziezm", "type": "section", "content": {"containerBackgroundColor": "#fcfcfc", "title": ""}, "handlers": {}, "isCodeManaged": false, "parentId": "gzl9y3qtexw7vmwg", "position": 0}
|
|
18
|
+
{"id": "8kzm2mhcv5xd5ji2", "type": "heading", "content": {"text": "2. Paste Review"}, "handlers": {}, "isCodeManaged": false, "parentId": "p7golp2r05uziezm", "position": 0}
|
|
19
|
+
{"id": "po6m73qliiv7g2ep", "type": "textareainput", "binding": {"eventType": "wf-change", "stateRef": "customer_review"}, "content": {"label": "", "rows": "4"}, "handlers": {}, "isCodeManaged": false, "parentId": "p7golp2r05uziezm", "position": 1}
|
|
20
|
+
{"id": "zazp9q0cpsglynsb", "type": "button", "content": {"text": "3. Draft response"}, "handlers": {}, "isCodeManaged": false, "parentId": "29qur7xljy4faoqt", "position": 4}
|
|
21
|
+
{"id": "k9fn5wf1kqwxvugj", "type": "section", "content": {"title": "Response message"}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 1, "visible": {"binding": "", "expression": "custom", "reversed": false}}
|
|
10
22
|
{"id": "khbe69j233n3d9ot", "type": "message", "content": {"message": "@{progress_message}"}, "handlers": {}, "isCodeManaged": false, "parentId": "k9fn5wf1kqwxvugj", "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
11
23
|
{"id": "z2noh2qiejykjjlq", "type": "section", "content": {"containerBackgroundColor": "#F5F5F9", "containerShadow": "none", "separatorColor": "none", "title": ""}, "handlers": {}, "isCodeManaged": false, "parentId": "k9fn5wf1kqwxvugj", "position": 1, "visible": {"binding": "review_response", "expression": "custom", "reversed": false}}
|
|
12
24
|
{"id": "sbzcwvq5e0z386cq", "type": "text", "content": {"text": "@{review_response}", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "z2noh2qiejykjjlq", "position": 0, "visible": {"binding": "", "expression": "custom", "reversed": false}}
|
|
13
|
-
{"id": "coz1oojtyzs0slm0", "type": "section", "content": {"containerBackgroundColor": "#
|
|
25
|
+
{"id": "coz1oojtyzs0slm0", "type": "section", "content": {"containerBackgroundColor": "#fcfcfc", "containerShadow": "none", "separatorColor": "none", "title": ""}, "handlers": {}, "isCodeManaged": false, "parentId": "k9fn5wf1kqwxvugj", "position": 2, "visible": {"binding": "review_response", "expression": "custom", "reversed": true}}
|
|
14
26
|
{"id": "akkcjdnrtudjd704", "type": "text", "content": {"text": "The response will be shown here.", "useMarkdown": "yes"}, "handlers": {}, "isCodeManaged": false, "parentId": "coz1oojtyzs0slm0", "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
15
|
-
{"id": "nbr03edevbgnhi29", "type": "text", "content": {"alignment": "center", "primaryTextColor": "#828282", "text": "This is just an example agent -
|
|
27
|
+
{"id": "nbr03edevbgnhi29", "type": "text", "content": {"alignment": "center", "primaryTextColor": "#828282", "text": "This is just an example agent - just delete the page and create a new one to start experimenting."}, "handlers": {}, "isCodeManaged": false, "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134", "position": 2}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id": "root", "type": "root", "content": {"appName": "
|
|
1
|
+
{"id": "root", "type": "root", "content": {"appName": "lucky-jellyfish"}, "handlers": {}, "isCodeManaged": false, "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id": "workflows_root", "type": "workflows_root", "content": {}, "handlers": {}, "isCodeManaged": false, "position": 0, "visible": {"binding": "", "expression": true, "reversed": false}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id": "lfltcky7l1fsm6j2", "type": "workflows_workflow", "content": {}, "handlers": {}, "parentId": "workflows_root", "position": 0}
|
|
@@ -3,11 +3,14 @@ import writer as wf
|
|
|
3
3
|
# Shows in the log when the app starts
|
|
4
4
|
# print("Hello world!")
|
|
5
5
|
|
|
6
|
+
# Or you can use a logger object
|
|
7
|
+
# logger.info("Successful startup")
|
|
8
|
+
|
|
6
9
|
# You can define functions which can be called from Python code blocks
|
|
7
|
-
def my_func():
|
|
8
|
-
return 1
|
|
10
|
+
# def my_func():
|
|
11
|
+
# return 1
|
|
9
12
|
|
|
10
13
|
# You can initialize state via code
|
|
11
|
-
initial_state = wf.init_state({
|
|
12
|
-
"my_var": 1337,
|
|
13
|
-
})
|
|
14
|
+
# initial_state = wf.init_state({
|
|
15
|
+
# "my_var": 1337,
|
|
16
|
+
# })
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
writer==0.8.3rc21
|
|
Binary file
|
|
@@ -3,6 +3,9 @@ import writer as wf
|
|
|
3
3
|
# Shows in the log when the app starts
|
|
4
4
|
# print("Hello world!")
|
|
5
5
|
|
|
6
|
+
# Or you can use a logger object
|
|
7
|
+
# logger.info("Successful startup")
|
|
8
|
+
|
|
6
9
|
# You can define functions which can be called from Python code blocks
|
|
7
10
|
def my_func():
|
|
8
11
|
return 1
|
writer/auth.py
CHANGED
|
@@ -82,7 +82,7 @@ class BasicAuth(Auth):
|
|
|
82
82
|
>>> login=os.getenv('LOGIN'),
|
|
83
83
|
>>> password=os.getenv('PASSWORD'),
|
|
84
84
|
>>> delay_after_failure=5,
|
|
85
|
-
>>>
|
|
85
|
+
>>> block_user_after_failure=False
|
|
86
86
|
>>> )
|
|
87
87
|
"""
|
|
88
88
|
login: str
|
|
@@ -405,6 +405,11 @@ def _client_ip(request: Request) -> str:
|
|
|
405
405
|
ip = x_forwarded_for.split(",")[0].strip()
|
|
406
406
|
else:
|
|
407
407
|
# Otherwise, use the direct connection IP
|
|
408
|
-
|
|
408
|
+
x_real_ip = request.headers.get("X-Real-IP")
|
|
409
|
+
if x_real_ip is not None:
|
|
410
|
+
ip = x_real_ip
|
|
411
|
+
else:
|
|
412
|
+
client = request.client
|
|
413
|
+
ip = client.host if client is not None else ""
|
|
409
414
|
|
|
410
415
|
return ip
|