22
33from __future__ import annotations
44
5- from typing import Any
5+ import base64
6+ from typing import TYPE_CHECKING , Any
67
78from weave .trace .call import Call
9+ from weave .trace .refs import ObjectRef
810from weave .trace .serialization .serialize import to_json
911from weave .trace .table import Table
10- from weave .trace .weave_client import WeaveClient
11- from weave .trace .widgets import Widget
12+ from weave .trace .widgets import ChildPredictionsWidget , ScoreSummaryWidget , Widget
13+ from weave .trace_server .interface .builtin_object_classes .call_view_spec import (
14+ CallViewSpec ,
15+ ChildPredictionsWidgetItem ,
16+ ContentViewItem ,
17+ ObjectRefViewItem ,
18+ ScoreSummaryWidgetItem ,
19+ TableRefViewItem ,
20+ )
21+ from weave .trace_server .interface .builtin_object_classes .call_view_spec import (
22+ ViewItem as CallViewSpecItem ,
23+ )
1224from weave .type_wrappers .Content .content import Content
1325
14- ViewItem = Content | str | Widget | Table
26+ if TYPE_CHECKING :
27+ from weave .trace .weave_client import WeaveClient
28+
29+ ViewItem = Content | str | Widget | Table | ObjectRef
1530ViewSpec = ViewItem | list [ViewItem ]
1631
1732
@@ -148,6 +163,101 @@ def serialize_view_spec(
148163 )
149164
150165
166+ def _sdk_view_item_to_call_view_spec_item (
167+ item : ViewItem ,
168+ client : WeaveClient ,
169+ * ,
170+ extension : str | None = None ,
171+ mimetype : str | None = None ,
172+ metadata : dict [str , Any ] | None = None ,
173+ encoding : str = "utf-8" ,
174+ ) -> CallViewSpecItem :
175+ """Convert an SDK view item to a CallViewSpec item for storage.
176+
177+ Args:
178+ item: A Content, string, Widget, or Table to convert.
179+ client: The WeaveClient for saving tables.
180+ extension: Optional file extension for string content.
181+ mimetype: Optional MIME type for string content.
182+ metadata: Optional metadata for string content.
183+ encoding: Encoding for string content.
184+
185+ Returns:
186+ A CallViewSpec item (ContentViewItem, WidgetItem, or TableRefViewItem).
187+ """
188+ if isinstance (item , ScoreSummaryWidget ):
189+ return ScoreSummaryWidgetItem ()
190+ elif isinstance (item , ChildPredictionsWidget ):
191+ return ChildPredictionsWidgetItem ()
192+ elif isinstance (item , Widget ):
193+ # Future widgets - default to score_summary for now
194+ return ScoreSummaryWidgetItem ()
195+ elif isinstance (item , Table ):
196+ # Publish the table and return its ref URI
197+ table_ref = client ._save_table (item )
198+ return TableRefViewItem (uri = table_ref .uri ())
199+ elif isinstance (item , ObjectRef ):
200+ # Store object references (e.g., SavedView) as URI strings
201+ return ObjectRefViewItem (uri = item .uri ())
202+ elif isinstance (item , (Content , str )):
203+ content_obj = resolve_view_content (
204+ item ,
205+ extension = extension ,
206+ mimetype = mimetype ,
207+ metadata = metadata ,
208+ encoding = encoding ,
209+ )
210+ # Encode content data as base64
211+ data_bytes = (
212+ content_obj .data
213+ if isinstance (content_obj .data , bytes )
214+ else content_obj .data .encode (content_obj .encoding or "utf-8" )
215+ )
216+ return ContentViewItem (
217+ mimetype = content_obj .mimetype ,
218+ encoding = content_obj .encoding ,
219+ data = base64 .b64encode (data_bytes ).decode ("ascii" ),
220+ metadata = content_obj .metadata ,
221+ )
222+ else :
223+ raise TypeError (f"Unsupported view item type: { type (item )} " )
224+
225+
226+ def _sdk_view_spec_to_call_view_spec_item (
227+ spec : ViewSpec ,
228+ client : WeaveClient ,
229+ * ,
230+ extension : str | None = None ,
231+ mimetype : str | None = None ,
232+ metadata : dict [str , Any ] | None = None ,
233+ encoding : str = "utf-8" ,
234+ ) -> CallViewSpecItem | list [CallViewSpecItem ]:
235+ """Convert an SDK view spec to CallViewSpec item(s).
236+
237+ Args:
238+ spec: A single view item or list of view items.
239+ client: The WeaveClient for saving tables.
240+ extension: Optional file extension for string content.
241+ mimetype: Optional MIME type for string content.
242+ metadata: Optional metadata for string content.
243+ encoding: Encoding for string content.
244+
245+ Returns:
246+ A CallViewSpec item or list of items.
247+ """
248+ if isinstance (spec , list ):
249+ return [_sdk_view_item_to_call_view_spec_item (item , client ) for item in spec ]
250+ else :
251+ return _sdk_view_item_to_call_view_spec_item (
252+ spec ,
253+ client ,
254+ extension = extension ,
255+ mimetype = mimetype ,
256+ metadata = metadata ,
257+ encoding = encoding ,
258+ )
259+
260+
151261def set_call_view (
152262 * ,
153263 call : Call ,
@@ -159,12 +269,16 @@ def set_call_view(
159269 metadata : dict [str , Any ] | None = None ,
160270 encoding : str = "utf-8" ,
161271) -> None :
162- """Attach serialized content to the provided call's summary under `weave.views`.
272+ """Attach a view to the call's pending views for later storage as CallViewSpec.
273+
274+ Views are stored in the call's _pending_views dict and will be converted to
275+ a CallViewSpec object when the call is finished. This allows deduplication
276+ of identical view configurations across calls.
163277
164278 Args:
165- call: The call whose summary should receive the view entry.
279+ call: The call whose pending views should receive the view entry.
166280 client: The active ``WeaveClient`` used for serialization.
167- name: Key to store the view under within ``summary.weave.views`` .
281+ name: Key to store the view under.
168282 content: A ``weave.Content``, raw text, ``Widget``, ``Table``, or list of these
169283 to serialize into the view.
170284 extension: Optional file extension used when converting text content.
@@ -189,35 +303,46 @@ def set_call_view(
189303 ... extension="html",
190304 ... )
191305 """
192- if call .summary is None :
193- call .summary = {}
194-
195- summary = call .summary
196-
197- weave_bucket = summary .get ("weave" )
198- if not isinstance (weave_bucket , dict ):
199- weave_bucket = {}
200- summary ["weave" ] = weave_bucket
201-
202- legacy_views = summary .get ("views" )
203- if isinstance (legacy_views , dict ):
204- summary .pop ("views" , None )
205- views = weave_bucket .get ("views" )
206-
207- if not isinstance (views , dict ):
208- views = {}
209- weave_bucket ["views" ] = views
210-
211- if isinstance (legacy_views , dict ):
212- views .update (legacy_views )
213-
214- project_id = client ._project_id ()
215- views [name ] = serialize_view_spec (
306+ # Store the converted view item in _pending_views
307+ call ._pending_views [name ] = _sdk_view_spec_to_call_view_spec_item (
216308 content ,
217- project_id ,
218309 client ,
219310 extension = extension ,
220311 mimetype = mimetype ,
221312 metadata = metadata ,
222313 encoding = encoding ,
223314 )
315+
316+
317+ def build_and_save_call_view_spec (
318+ call : Call ,
319+ client : WeaveClient ,
320+ ) -> str | None :
321+ """Build a CallViewSpec from pending views and save it as an object.
322+
323+ This function collects all pending views from the call, creates a CallViewSpec
324+ object, and saves it to the project. The returned ref URI can be used as the
325+ view_spec_ref in the call_end request.
326+
327+ Args:
328+ call: The call with pending views to save.
329+ client: The WeaveClient for saving the object.
330+
331+ Returns:
332+ The view_spec_ref URI string, or None if no views are pending.
333+ """
334+ if not call ._pending_views :
335+ return None
336+
337+ # Convert pending views dict values to the proper union type
338+ views_dict : dict [str , CallViewSpecItem | list [CallViewSpecItem ]] = {}
339+ for name , item in call ._pending_views .items ():
340+ views_dict [name ] = item
341+
342+ # Create the CallViewSpec object
343+ view_spec = CallViewSpec (views = views_dict )
344+
345+ # Save as a versioned object - content-addressed storage will deduplicate
346+ ref : ObjectRef = client ._save_object (view_spec , name = "CallViewSpec" )
347+
348+ return ref .uri ()
0 commit comments