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/ai/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
from contextvars import ContextVar
|
|
4
4
|
from datetime import datetime
|
|
5
|
+
from functools import wraps
|
|
5
6
|
from typing import (
|
|
6
7
|
Any,
|
|
7
8
|
Callable,
|
|
@@ -13,6 +14,7 @@ from typing import (
|
|
|
13
14
|
Optional,
|
|
14
15
|
Set,
|
|
15
16
|
TypedDict,
|
|
17
|
+
TypeVar,
|
|
16
18
|
Union,
|
|
17
19
|
cast,
|
|
18
20
|
)
|
|
@@ -20,7 +22,7 @@ from uuid import uuid4
|
|
|
20
22
|
|
|
21
23
|
from httpx import Timeout
|
|
22
24
|
from writerai import DefaultHttpxClient, Writer
|
|
23
|
-
from writerai._exceptions import WriterError
|
|
25
|
+
from writerai._exceptions import BadRequestError, WriterError
|
|
24
26
|
from writerai._response import BinaryAPIResponse
|
|
25
27
|
from writerai._streaming import Stream
|
|
26
28
|
from writerai._types import Body, Headers, NotGiven, Query
|
|
@@ -45,7 +47,14 @@ from writerai.types.applications import (
|
|
|
45
47
|
JobRetryResponse,
|
|
46
48
|
)
|
|
47
49
|
from writerai.types.applications.application_graphs_response import ApplicationGraphsResponse
|
|
48
|
-
from writerai.types.chat_chat_params import
|
|
50
|
+
from writerai.types.chat_chat_params import (
|
|
51
|
+
GraphData,
|
|
52
|
+
MessageContentMixedContentImageFragment,
|
|
53
|
+
MessageContentMixedContentImageFragmentImageURL,
|
|
54
|
+
MessageContentMixedContentTextFragment,
|
|
55
|
+
ResponseFormat,
|
|
56
|
+
ToolChoice,
|
|
57
|
+
)
|
|
49
58
|
from writerai.types.chat_chat_params import Message as WriterAIMessage
|
|
50
59
|
from writerai.types.chat_completion_message import ChatCompletionMessage
|
|
51
60
|
from writerai.types.question import Question
|
|
@@ -53,14 +62,23 @@ from writerai.types.question_response_chunk import QuestionResponseChunk
|
|
|
53
62
|
from writerai.types.shared_params.tool_param import FunctionTool as SDKFunctionTool
|
|
54
63
|
from writerai.types.shared_params.tool_param import GraphTool as SDKGraphTool
|
|
55
64
|
from writerai.types.shared_params.tool_param import LlmTool as SDKLlmTool
|
|
65
|
+
from writerai.types.shared_params.tool_param import WebSearchTool as SDKWebSearchTool
|
|
56
66
|
|
|
57
67
|
from writer.core import get_app_process
|
|
58
68
|
|
|
59
|
-
DEFAULT_CHAT_MODEL = "palmyra-
|
|
60
|
-
DEFAULT_COMPLETION_MODEL = "palmyra-
|
|
69
|
+
DEFAULT_CHAT_MODEL = "palmyra-x5"
|
|
70
|
+
DEFAULT_COMPLETION_MODEL = "palmyra-x5"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_ai_client: ContextVar[Optional[Writer]] = ContextVar(
|
|
74
|
+
"ai_client", default=None
|
|
75
|
+
)
|
|
61
76
|
|
|
62
77
|
|
|
63
|
-
|
|
78
|
+
class ExtendedWebSearchTool(TypedDict, total=False):
|
|
79
|
+
"""Extended web search tool that includes all fields supported by the API"""
|
|
80
|
+
type: Literal["web_search"]
|
|
81
|
+
function: Dict[str, Any] # Flexible to support include_raw_content and other fields
|
|
64
82
|
|
|
65
83
|
|
|
66
84
|
class APIOptions(TypedDict, total=False):
|
|
@@ -75,10 +93,11 @@ class ChatOptions(APIOptions, total=False):
|
|
|
75
93
|
tool_choice: ToolChoice
|
|
76
94
|
tools: Union[
|
|
77
95
|
Iterable[
|
|
78
|
-
Union[SDKGraphTool, SDKFunctionTool, SDKLlmTool]
|
|
96
|
+
Union[SDKGraphTool, SDKFunctionTool, SDKLlmTool, SDKWebSearchTool]
|
|
79
97
|
],
|
|
80
98
|
NotGiven
|
|
81
99
|
]
|
|
100
|
+
response_format: Union[ResponseFormat, NotGiven]
|
|
82
101
|
logprobs: Union[bool, NotGiven]
|
|
83
102
|
max_tokens: Union[int, NotGiven]
|
|
84
103
|
n: Union[int, NotGiven]
|
|
@@ -166,6 +185,12 @@ class LLMTool(Tool):
|
|
|
166
185
|
description: str
|
|
167
186
|
|
|
168
187
|
|
|
188
|
+
class WebSearchTool(Tool):
|
|
189
|
+
include_domains: Optional[List[str]]
|
|
190
|
+
exclude_domains: Optional[List[str]]
|
|
191
|
+
include_raw_content: Optional[bool]
|
|
192
|
+
|
|
193
|
+
|
|
169
194
|
def _process_completion_data_chunk(choice: CompletionChunk) -> str:
|
|
170
195
|
text = choice.value
|
|
171
196
|
if not text:
|
|
@@ -451,7 +476,12 @@ class Graph(SDKWrapper):
|
|
|
451
476
|
if description:
|
|
452
477
|
payload["description"] = description
|
|
453
478
|
graphs = self._retrieve_graphs_accessor()
|
|
454
|
-
response = graphs.update(
|
|
479
|
+
response = graphs.update(
|
|
480
|
+
self.id,
|
|
481
|
+
name=payload.get("name", NotGiven()),
|
|
482
|
+
description=payload.get("description", NotGiven()),
|
|
483
|
+
**config
|
|
484
|
+
)
|
|
455
485
|
Graph.stale_ids.add(self.id)
|
|
456
486
|
return response
|
|
457
487
|
|
|
@@ -1013,6 +1043,39 @@ def delete_file(
|
|
|
1013
1043
|
return files.delete(file_id, **config)
|
|
1014
1044
|
|
|
1015
1045
|
|
|
1046
|
+
class GuardrailError(Exception):
|
|
1047
|
+
def __init__(self, name: str, message: str, *args):
|
|
1048
|
+
super().__init__(f"{message}: {name}", *args)
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
R = TypeVar("R")
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
def catch_guardrail_error(func: Callable[..., R]) -> Callable[..., R]:
|
|
1055
|
+
@wraps(func)
|
|
1056
|
+
def wrapper(*args: Any, **kwargs: Any) -> R:
|
|
1057
|
+
try:
|
|
1058
|
+
return func(*args, **kwargs)
|
|
1059
|
+
except BadRequestError as e:
|
|
1060
|
+
parsed = e.response.json()
|
|
1061
|
+
errors = parsed.get("errors")
|
|
1062
|
+
if not errors:
|
|
1063
|
+
raise
|
|
1064
|
+
|
|
1065
|
+
extras = parsed.get("extras")
|
|
1066
|
+
if extras is None:
|
|
1067
|
+
raise
|
|
1068
|
+
guardrail_info = extras.get("guardrail_info")
|
|
1069
|
+
if guardrail_info is None:
|
|
1070
|
+
raise
|
|
1071
|
+
|
|
1072
|
+
raise GuardrailError(
|
|
1073
|
+
name=guardrail_info["guardrail_name"],
|
|
1074
|
+
message=errors[0]["description"],
|
|
1075
|
+
) from None
|
|
1076
|
+
|
|
1077
|
+
return wrapper
|
|
1078
|
+
|
|
1016
1079
|
class Conversation:
|
|
1017
1080
|
"""
|
|
1018
1081
|
Manages messages within a conversation flow with an AI system,
|
|
@@ -1107,17 +1170,24 @@ class Conversation:
|
|
|
1107
1170
|
the `temperature` to 0.7 for this specific call.
|
|
1108
1171
|
|
|
1109
1172
|
"""
|
|
1173
|
+
class ContentFragment(TypedDict, total=False):
|
|
1174
|
+
"""Content fragment for messages that can contain text or images."""
|
|
1175
|
+
type: Literal["text", "image_url"]
|
|
1176
|
+
text: Optional[str]
|
|
1177
|
+
image_url: Optional[Dict[str, str]]
|
|
1178
|
+
|
|
1110
1179
|
class Message(TypedDict, total=False):
|
|
1111
1180
|
"""
|
|
1112
1181
|
Typed dictionary for conversation messages.
|
|
1113
1182
|
|
|
1114
1183
|
:param role: Specifies the sender role.
|
|
1115
|
-
:param content: Text content of the message
|
|
1184
|
+
:param content: Text content of the message or array of content fragments
|
|
1185
|
+
for multimodal messages.
|
|
1116
1186
|
:param actions: Optional dictionary containing actions
|
|
1117
1187
|
related to the message.
|
|
1118
1188
|
"""
|
|
1119
1189
|
role: Literal["system", "assistant", "user", "tool"]
|
|
1120
|
-
content: str
|
|
1190
|
+
content: Union[str, List['Conversation.ContentFragment']]
|
|
1121
1191
|
actions: Optional[dict]
|
|
1122
1192
|
name: Optional[str]
|
|
1123
1193
|
tool_call_id: Optional[str]
|
|
@@ -1144,13 +1214,44 @@ class Conversation:
|
|
|
1144
1214
|
f"Improper message format to add to Conversation: {message}"
|
|
1145
1215
|
)
|
|
1146
1216
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1217
|
+
content = message["content"]
|
|
1218
|
+
if isinstance(content, list):
|
|
1219
|
+
# Validate multimodal content structure
|
|
1220
|
+
for fragment in content:
|
|
1221
|
+
if not isinstance(fragment, dict):
|
|
1222
|
+
raise ValueError(
|
|
1223
|
+
f"Invalid content fragment in message: {message}. "
|
|
1224
|
+
f"Fragments must be dictionaries."
|
|
1225
|
+
)
|
|
1226
|
+
fragment_type = fragment.get("type")
|
|
1227
|
+
if fragment_type not in ["text", "image_url"]:
|
|
1228
|
+
raise ValueError(
|
|
1229
|
+
f"Invalid fragment type '{fragment_type}' in message: {message}. "
|
|
1230
|
+
f"Type must be 'text' or 'image_url'."
|
|
1231
|
+
)
|
|
1232
|
+
if fragment_type == "text" and "text" not in fragment:
|
|
1233
|
+
raise ValueError(
|
|
1234
|
+
f"Text fragment missing 'text' field in message: {message}"
|
|
1235
|
+
)
|
|
1236
|
+
if fragment_type == "image_url" and "image_url" not in fragment:
|
|
1237
|
+
raise ValueError(
|
|
1238
|
+
f"Image fragment missing 'image_url' field in message: {message}"
|
|
1239
|
+
)
|
|
1240
|
+
if fragment_type == "image_url" and not isinstance(fragment.get("image_url"), dict):
|
|
1241
|
+
raise ValueError(
|
|
1242
|
+
f"Image fragment 'image_url' must be a dict in message: {message}"
|
|
1243
|
+
)
|
|
1244
|
+
if fragment_type == "image_url" and "url" not in fragment.get("image_url", {}):
|
|
1245
|
+
raise ValueError(
|
|
1246
|
+
f"Image fragment missing 'url' in 'image_url' in message: {message}"
|
|
1247
|
+
)
|
|
1248
|
+
elif not (
|
|
1249
|
+
isinstance(content, str)
|
|
1250
|
+
or content is None
|
|
1151
1251
|
):
|
|
1152
1252
|
raise ValueError(
|
|
1153
|
-
f"
|
|
1253
|
+
f"Invalid content format in message: {message}. "
|
|
1254
|
+
f"Content must be a string, None, or array of content fragments."
|
|
1154
1255
|
)
|
|
1155
1256
|
|
|
1156
1257
|
if message["role"] not in ["system", "assistant", "user", "tool"]:
|
|
@@ -1232,7 +1333,24 @@ class Conversation:
|
|
|
1232
1333
|
clear_chunk = _clear_chunk_flag(raw_chunk)
|
|
1233
1334
|
updated_last_message: 'Conversation.Message' = self.messages[-1]
|
|
1234
1335
|
if "content" in clear_chunk:
|
|
1235
|
-
|
|
1336
|
+
new_content = clear_chunk.pop("content") or ""
|
|
1337
|
+
if isinstance(updated_last_message["content"], list):
|
|
1338
|
+
# Handle list content (multimodal)
|
|
1339
|
+
if isinstance(new_content, str) and new_content:
|
|
1340
|
+
# Find the last text fragment and append to it, or create new one
|
|
1341
|
+
last_text_fragment = None
|
|
1342
|
+
for i in range(len(updated_last_message["content"]) - 1, -1, -1):
|
|
1343
|
+
if updated_last_message["content"][i].get("type") == "text":
|
|
1344
|
+
last_text_fragment = updated_last_message["content"][i]
|
|
1345
|
+
break
|
|
1346
|
+
|
|
1347
|
+
if last_text_fragment:
|
|
1348
|
+
last_text_fragment["text"] = (last_text_fragment.get("text") or "") + new_content
|
|
1349
|
+
else:
|
|
1350
|
+
updated_last_message["content"].append({"type": "text", "text": new_content})
|
|
1351
|
+
else:
|
|
1352
|
+
# Handle string content
|
|
1353
|
+
updated_last_message["content"] = str(updated_last_message["content"]) + str(new_content)
|
|
1236
1354
|
|
|
1237
1355
|
if "tool_calls" in clear_chunk:
|
|
1238
1356
|
# Ensure 'tool_calls' exists in updated_last_message as list
|
|
@@ -1290,15 +1408,48 @@ class Conversation:
|
|
|
1290
1408
|
Converts a message object stored in Conversation to a Writer AI SDK
|
|
1291
1409
|
`Message` model, suitable for calls to API.
|
|
1292
1410
|
|
|
1293
|
-
:param
|
|
1411
|
+
:param message: The message to prepare.
|
|
1294
1412
|
:raises ValueError: If there are no messages in the conversation
|
|
1295
1413
|
to merge with.
|
|
1296
1414
|
"""
|
|
1297
1415
|
if not ("role" in message and "content" in message):
|
|
1298
1416
|
raise ValueError("Improper message format")
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1417
|
+
|
|
1418
|
+
content = message.get("content")
|
|
1419
|
+
if isinstance(content, list):
|
|
1420
|
+
# Handle multimodal content (text + images)
|
|
1421
|
+
# Convert our ContentFragment format to SDK format
|
|
1422
|
+
sdk_content: List[Union[MessageContentMixedContentTextFragment, MessageContentMixedContentImageFragment]] = []
|
|
1423
|
+
for fragment in content:
|
|
1424
|
+
if fragment.get("type") == "text":
|
|
1425
|
+
sdk_content.append({
|
|
1426
|
+
"type": "text",
|
|
1427
|
+
"text": fragment.get("text") or ""
|
|
1428
|
+
})
|
|
1429
|
+
elif fragment.get("type") == "image_url":
|
|
1430
|
+
image_url_data = fragment.get("image_url", {})
|
|
1431
|
+
if isinstance(image_url_data, dict):
|
|
1432
|
+
# Extract the URL from the dict structure
|
|
1433
|
+
url = image_url_data.get("url", "")
|
|
1434
|
+
else:
|
|
1435
|
+
# Assume it's already a URL string
|
|
1436
|
+
url = str(image_url_data) if image_url_data else ""
|
|
1437
|
+
|
|
1438
|
+
image_url_obj: MessageContentMixedContentImageFragmentImageURL = {"url": url}
|
|
1439
|
+
sdk_content.append({
|
|
1440
|
+
"type": "image_url",
|
|
1441
|
+
"image_url": image_url_obj
|
|
1442
|
+
})
|
|
1443
|
+
|
|
1444
|
+
sdk_message = WriterAIMessage(
|
|
1445
|
+
content=sdk_content,
|
|
1446
|
+
role=message["role"]
|
|
1447
|
+
)
|
|
1448
|
+
else:
|
|
1449
|
+
# Handle simple text content
|
|
1450
|
+
sdk_message = WriterAIMessage(
|
|
1451
|
+
content=content or "",
|
|
1452
|
+
role=message["role"]
|
|
1302
1453
|
)
|
|
1303
1454
|
if msg_name := message.get("name"):
|
|
1304
1455
|
sdk_message["name"] = cast(str, msg_name)
|
|
@@ -1383,8 +1534,8 @@ class Conversation:
|
|
|
1383
1534
|
|
|
1384
1535
|
def _prepare_tool(
|
|
1385
1536
|
self,
|
|
1386
|
-
tool_instance: Union['Graph', GraphTool, FunctionTool, LLMTool]
|
|
1387
|
-
) -> Union[SDKGraphTool, SDKFunctionTool, SDKLlmTool]:
|
|
1537
|
+
tool_instance: Union['Graph', GraphTool, FunctionTool, LLMTool, WebSearchTool, dict]
|
|
1538
|
+
) -> Union[SDKGraphTool, SDKFunctionTool, SDKLlmTool, SDKWebSearchTool]:
|
|
1388
1539
|
"""
|
|
1389
1540
|
Internal helper function to process a tool instance
|
|
1390
1541
|
into the required format.
|
|
@@ -1624,6 +1775,32 @@ class Conversation:
|
|
|
1624
1775
|
}
|
|
1625
1776
|
}
|
|
1626
1777
|
)
|
|
1778
|
+
elif tool_type == "web_search":
|
|
1779
|
+
# Return web search tool JSON - SDK format
|
|
1780
|
+
tool_instance = cast(WebSearchTool, tool_instance)
|
|
1781
|
+
function_config: Dict[str, Any] = {}
|
|
1782
|
+
|
|
1783
|
+
# Add required parameters - SDK expects these as lists
|
|
1784
|
+
if "include_domains" in tool_instance:
|
|
1785
|
+
include_domains = tool_instance["include_domains"]
|
|
1786
|
+
if include_domains:
|
|
1787
|
+
function_config["include_domains"] = include_domains
|
|
1788
|
+
if "exclude_domains" in tool_instance:
|
|
1789
|
+
exclude_domains = tool_instance["exclude_domains"]
|
|
1790
|
+
if exclude_domains:
|
|
1791
|
+
function_config["exclude_domains"] = exclude_domains
|
|
1792
|
+
if "include_raw_content" in tool_instance:
|
|
1793
|
+
include_raw_content = tool_instance["include_raw_content"]
|
|
1794
|
+
if include_raw_content is not None:
|
|
1795
|
+
function_config["include_raw_content"] = include_raw_content
|
|
1796
|
+
|
|
1797
|
+
# Return as ExtendedWebSearchTool but ensure SDK compatibility
|
|
1798
|
+
result = ExtendedWebSearchTool({
|
|
1799
|
+
"type": "web_search",
|
|
1800
|
+
"function": function_config
|
|
1801
|
+
})
|
|
1802
|
+
# Cast to SDKWebSearchTool for SDK compatibility while preserving extra fields
|
|
1803
|
+
return cast(SDKWebSearchTool, result)
|
|
1627
1804
|
else:
|
|
1628
1805
|
raise ValueError(f"Unsupported tool type: {tool_type}")
|
|
1629
1806
|
|
|
@@ -1676,6 +1853,37 @@ class Conversation:
|
|
|
1676
1853
|
"""
|
|
1677
1854
|
self.__add__({"role": role, "content": message})
|
|
1678
1855
|
|
|
1856
|
+
def add_with_images(
|
|
1857
|
+
self,
|
|
1858
|
+
role: Literal["system", "assistant", "user", "tool"],
|
|
1859
|
+
text: Optional[str] = None,
|
|
1860
|
+
image_urls: Optional[List[str]] = None
|
|
1861
|
+
):
|
|
1862
|
+
"""
|
|
1863
|
+
Adds a new multimodal message with text and/or images.
|
|
1864
|
+
|
|
1865
|
+
:param role: The role of the message sender.
|
|
1866
|
+
:param text: Optional text content.
|
|
1867
|
+
:param image_urls: Optional list of image URLs (web URLs or base64 data URLs).
|
|
1868
|
+
"""
|
|
1869
|
+
if not text and not image_urls:
|
|
1870
|
+
raise ValueError("At least one of text or image_urls must be provided")
|
|
1871
|
+
|
|
1872
|
+
content: List['Conversation.ContentFragment'] = []
|
|
1873
|
+
|
|
1874
|
+
if text:
|
|
1875
|
+
content.append({"type": "text", "text": text})
|
|
1876
|
+
|
|
1877
|
+
if image_urls:
|
|
1878
|
+
for image_url in image_urls:
|
|
1879
|
+
content.append({
|
|
1880
|
+
"type": "image_url",
|
|
1881
|
+
"image_url": {"url": image_url}
|
|
1882
|
+
})
|
|
1883
|
+
|
|
1884
|
+
self.__add__({"role": role, "content": content})
|
|
1885
|
+
|
|
1886
|
+
@catch_guardrail_error
|
|
1679
1887
|
def _send_chat_request(
|
|
1680
1888
|
self,
|
|
1681
1889
|
request_model: str,
|
|
@@ -1701,13 +1909,20 @@ class Conversation:
|
|
|
1701
1909
|
f"prepared messages – {prepared_messages}, " +
|
|
1702
1910
|
f"request_data – {request_data}"
|
|
1703
1911
|
)
|
|
1912
|
+
tools = request_data.get('tools', NotGiven())
|
|
1913
|
+
tool_choice: Union[ToolChoice, NotGiven]
|
|
1914
|
+
if isinstance(tools, NotGiven):
|
|
1915
|
+
tool_choice = NotGiven()
|
|
1916
|
+
else:
|
|
1917
|
+
tool_choice = request_data.get('tool_choice', cast(ToolChoice, 'auto'))
|
|
1704
1918
|
return client.chat.chat(
|
|
1705
1919
|
messages=prepared_messages,
|
|
1706
1920
|
model=request_model,
|
|
1707
1921
|
stream=stream,
|
|
1708
1922
|
logprobs=request_data.get('logprobs', NotGiven()),
|
|
1709
|
-
tools=
|
|
1710
|
-
tool_choice=
|
|
1923
|
+
tools=tools,
|
|
1924
|
+
tool_choice=tool_choice,
|
|
1925
|
+
response_format=request_data.get('response_format', NotGiven()),
|
|
1711
1926
|
max_tokens=request_data.get('max_tokens', NotGiven()),
|
|
1712
1927
|
n=request_data.get('n', NotGiven()),
|
|
1713
1928
|
stop=request_data.get('stop', NotGiven()),
|
|
@@ -2124,6 +2339,7 @@ class Conversation:
|
|
|
2124
2339
|
] # can be an instance of tool or a list of instances
|
|
2125
2340
|
] = None,
|
|
2126
2341
|
max_tool_depth: int = 5,
|
|
2342
|
+
response_format: Optional[ResponseFormat] = None
|
|
2127
2343
|
) -> 'Conversation.Message':
|
|
2128
2344
|
"""
|
|
2129
2345
|
Processes the conversation with the current messages and additional
|
|
@@ -2134,6 +2350,7 @@ class Conversation:
|
|
|
2134
2350
|
:param tools: Optional tools to use for processing.
|
|
2135
2351
|
:param config: Optional parameters to pass for processing.
|
|
2136
2352
|
:param max_tool_depth: Maximum depth for tool calls processing.
|
|
2353
|
+
:param response_format: Optional JSON schema used to format the model's output.
|
|
2137
2354
|
:return: Generated message.
|
|
2138
2355
|
:raises RuntimeError: If response data was not properly formatted
|
|
2139
2356
|
to retrieve model text.
|
|
@@ -2150,6 +2367,13 @@ class Conversation:
|
|
|
2150
2367
|
request_data: ChatOptions = {**config, **self.config}
|
|
2151
2368
|
if prepared_tools:
|
|
2152
2369
|
request_data |= {"tools": prepared_tools}
|
|
2370
|
+
if response_format:
|
|
2371
|
+
if not isinstance(response_format, dict):
|
|
2372
|
+
raise ValueError(
|
|
2373
|
+
"Invalid schema for response_format: "
|
|
2374
|
+
f"dictionary required, got {type(response_format)}"
|
|
2375
|
+
)
|
|
2376
|
+
request_data |= {"response_format": response_format}
|
|
2153
2377
|
request_model = \
|
|
2154
2378
|
request_data.get("model") or WriterAIManager.use_chat_model()
|
|
2155
2379
|
|
|
@@ -2184,7 +2408,8 @@ class Conversation:
|
|
|
2184
2408
|
List[Union[Graph, GraphTool, FunctionTool]]
|
|
2185
2409
|
] # can be an instance of tool or a list of instances
|
|
2186
2410
|
] = None,
|
|
2187
|
-
max_tool_depth: int = 5
|
|
2411
|
+
max_tool_depth: int = 5,
|
|
2412
|
+
response_format: Optional[ResponseFormat] = None
|
|
2188
2413
|
) -> Generator[dict, None, None]:
|
|
2189
2414
|
"""
|
|
2190
2415
|
Initiates a stream to receive chunks of the model's reply.
|
|
@@ -2208,6 +2433,13 @@ class Conversation:
|
|
|
2208
2433
|
request_data: ChatOptions = {**config, **self.config}
|
|
2209
2434
|
if prepared_tools:
|
|
2210
2435
|
request_data |= {"tools": prepared_tools}
|
|
2436
|
+
if response_format:
|
|
2437
|
+
if not isinstance(response_format, dict):
|
|
2438
|
+
raise ValueError(
|
|
2439
|
+
"Invalid schema for response_format: "
|
|
2440
|
+
f"dictionary required, got {type(response_format)}"
|
|
2441
|
+
)
|
|
2442
|
+
request_data |= {"response_format": response_format}
|
|
2211
2443
|
request_model = \
|
|
2212
2444
|
request_data.get("model") or WriterAIManager.use_chat_model()
|
|
2213
2445
|
|
|
@@ -2341,7 +2573,7 @@ class Apps:
|
|
|
2341
2573
|
def generate_content(
|
|
2342
2574
|
self,
|
|
2343
2575
|
application_id: str,
|
|
2344
|
-
input_dict: Optional[Dict[str, str]] = None,
|
|
2576
|
+
input_dict: Optional[Dict[str, Optional[Union[List[str], str]]]] = None,
|
|
2345
2577
|
async_job: Optional[bool] = False,
|
|
2346
2578
|
config: Optional[APIOptions] = None
|
|
2347
2579
|
) -> Union[str, JobCreateResponse]:
|
|
@@ -2390,11 +2622,19 @@ class Apps:
|
|
|
2390
2622
|
... async_job=True
|
|
2391
2623
|
... )
|
|
2392
2624
|
>>> print(response)
|
|
2393
|
-
JobCreateResponse(
|
|
2625
|
+
JobCreateResponse(
|
|
2626
|
+
id="job_456",
|
|
2627
|
+
created_at=datetime(2025, 2, 24, 12, 30, 45),
|
|
2628
|
+
status="in_progress"
|
|
2629
|
+
)
|
|
2394
2630
|
>>> result = writer.ai.apps.retrieve_job(job_id=response.id)
|
|
2395
2631
|
>>> if result.status == "completed":
|
|
2396
2632
|
... print(result.data)
|
|
2397
|
-
{
|
|
2633
|
+
{
|
|
2634
|
+
"title": "output",
|
|
2635
|
+
"suggestion": "Climate change refers to long-term shifts in "
|
|
2636
|
+
"temperatures and weather patterns..."
|
|
2637
|
+
}
|
|
2398
2638
|
|
|
2399
2639
|
"""
|
|
2400
2640
|
|
|
@@ -2404,9 +2644,16 @@ class Apps:
|
|
|
2404
2644
|
inputs = []
|
|
2405
2645
|
|
|
2406
2646
|
for k, v in input_dict.items():
|
|
2647
|
+
# Convert None/empty to []
|
|
2648
|
+
# to avoid API 400 errors on optional inputs
|
|
2649
|
+
if v is None or v == "":
|
|
2650
|
+
value = []
|
|
2651
|
+
else:
|
|
2652
|
+
value = v if isinstance(v, list) else [v]
|
|
2653
|
+
|
|
2407
2654
|
inputs.append(Input({
|
|
2408
2655
|
"id": k,
|
|
2409
|
-
"value":
|
|
2656
|
+
"value": value
|
|
2410
2657
|
}))
|
|
2411
2658
|
|
|
2412
2659
|
if not async_job:
|
|
@@ -2707,6 +2954,7 @@ class Tools:
|
|
|
2707
2954
|
return result.entities
|
|
2708
2955
|
|
|
2709
2956
|
|
|
2957
|
+
@catch_guardrail_error
|
|
2710
2958
|
def complete(
|
|
2711
2959
|
initial_text: str,
|
|
2712
2960
|
config: Optional['CreateOptions'] = None
|
|
@@ -2753,6 +3001,7 @@ def complete(
|
|
|
2753
3001
|
f"{response_data}")
|
|
2754
3002
|
|
|
2755
3003
|
|
|
3004
|
+
@catch_guardrail_error
|
|
2756
3005
|
def stream_complete(
|
|
2757
3006
|
initial_text: str,
|
|
2758
3007
|
config: Optional['CreateOptions'] = None
|