Source code for apple_fm_sdk.transcript
# For licensing see accompanying LICENSE file.
# Copyright (C) 2026 Apple Inc. All Rights Reserved.
import json
import ctypes
from apple_fm_sdk.errors import _status_code_to_exception
from apple_fm_sdk.c_helpers import _get_error_string
try:
from . import _ctypes_bindings as lib
except ImportError:
raise ImportError(
"Foundation Models C bindings not found. Please ensure _foundationmodels_ctypes.py is available."
)
[docs]
class Transcript:
"""Represents a foundation model session's transcript.
A ``Transcript`` provides access to the complete session history of a
LanguageModelSession, including all user prompts, model responses, and tool
invocations. The transcript is automatically updated after each interaction
with the model.
**Transcript Format:**
The transcript follows the Foundation Models Swift framework structure and is
organized as a dictionary containing:
- **version**: Integer version number (currently 1)
- **type**: String identifier "FoundationModels.Transcript"
- **transcript**: Object containing:
- **entries**: List of session entries, each with:
- ``id``: Unique UUID for the entry
- ``role``: One of "instructions", "user", "response", or "tool"
- ``contents``: Array of content objects with ``type``, ``id``, and type-specific fields
**Entry Types by Role:**
- **instructions**: System instructions and tool definitions
- ``tools``: Array of available tool definitions
- ``contents``: Array with text instructions
- **user**: User messages and requests
- ``contents``: Array of content objects (text and more)
- ``options``: Optional configuration for the request
- ``responseFormat``: Optional structured output schema
- **response**: Model-generated responses
- ``toolCalls``: Array of tool invocations (if tools were called)
- ``contents``: Array of response content (text or structured data)
- ``assets``: Array of model asset identifiers used
- **tool**: Tool execution results
- ``toolName``: Name of the executed tool
- ``toolCallID``: UUID linking to the tool call
- ``contents``: Array with tool execution results
**When Transcripts Are Updated:**
- After each ``respond()`` call completes successfully
- After each ``stream_response()`` completes
- After tool invocations are processed
- NOT during streaming (only after completion)
- NOT if a request fails or is cancelled
Examples:
Accessing transcript after session::
import apple_fm_sdk as fm
session = fm.LanguageModelSession()
await session.respond("Hello!")
await session.respond("What is Python?")
# Get the full session history
transcript = await session.transcript.to_dict()
# Access the entries
entries = transcript["transcript"]["entries"]
for entry in entries:
role = entry["role"]
print(f"Entry role: {role}")
if "contents" in entry:
for content in entry["contents"]:
if content["type"] == "text":
print(f" Text: {content['text']}")
Monitoring session length::
import apple_fm_sdk as fm
session = fm.LanguageModelSession()
for i in range(5):
await session.respond(f"Question {i}")
transcript = await session.transcript.to_dict()
entry_count = len(transcript["transcript"]["entries"])
print(f"Session has {entry_count} entries")
Examining tool calls in transcript::
import apple_fm_sdk as fm
from my_tools import CalculatorTool
session = fm.LanguageModelSession(
tools=[CalculatorTool()]
)
await session.respond("What is 15 * 24?")
transcript = await session.transcript.to_dict()
# Find entries with tool calls
for entry in transcript["transcript"]["entries"]:
if entry["role"] == "response" and "toolCalls" in entry:
print(f"Tool calls: {entry['toolCalls']}")
elif entry["role"] == "tool":
print(f"Tool result for {entry['toolName']}")
Saving session history::
import apple_fm_sdk as fm
import json
session = fm.LanguageModelSession()
# Have a session
await session.respond("Hello")
await session.respond("Tell me about Python")
# Save transcript to file
transcript = await session.transcript.to_dict()
with open("session.json", "w") as f:
json.dump(transcript, f, indent=2)
Note:
- The transcript object shares the session's internal pointer
- Transcripts are read-only; you cannot modify session history
- Large sessions may result in large transcript dictionaries
- The transcript format follows the Foundation Models Swift framework structure
- Accessing the transcript does not affect the session state
See Also:
- :class:`~apple_fm_sdk.session.LanguageModelSession`: For creating sessions
- :meth:`~apple_fm_sdk.session.LanguageModelSession.respond`: For making requests
"""
[docs]
def __init__(
self,
_ptr,
):
"""Initialize a Transcript instance.
Note:
Transcript instances are automatically created by LanguageModelSession.
Do not create Transcript instances directly.
"""
# A transcript doesn't get it's own pointer, it uses the session's pointer
self.session_ptr = _ptr
[docs]
async def to_dict(self) -> dict:
"""Get the current transcript of the session as a dictionary.
This function retrieves the complete session history, including all
user messages, model responses, and tool interactions. The transcript
is returned as a structured dictionary following the Foundation Models
Swift framework format.
:return: The transcript data as a dictionary with the following structure:
- ``version`` (int): Version number (currently 1)
- ``type`` (str): Type identifier "FoundationModels.Transcript"
- ``transcript`` (dict): Object containing:
- ``entries`` (list): List of session entries, each with:
- ``id`` (str): Unique UUID for the entry
- ``role`` (str): One of "instructions", "user", "response", or "tool"
- ``contents`` (list): Array of content objects
- Additional role-specific fields (tools, toolCalls, options, and more)
:rtype: dict
:raises GenerationError: If fetching the transcript fails due to an internal error
Example:
::
import apple_fm_sdk as fm
session = fm.LanguageModelSession()
await session.respond("Hello!")
transcript = await session.transcript.to_dict()
# Access entries
entries = transcript["transcript"]["entries"]
for entry in entries:
print(f"{entry['role']}: {entry.get('id')}")
Note:
- This is an async function and must be awaited
- The returned dictionary is a snapshot; it won't update automatically
- Call this function again to get an updated transcript after new interactions
"""
error_code = ctypes.c_int32() # C error status code
error_description = ctypes.POINTER(
ctypes.c_char
)() # C error description pointer
jsn_string = lib.FMLanguageModelSessionGetTranscriptJSONString(
self.session_ptr, ctypes.byref(error_code), ctypes.byref(error_description)
)
# Check if we got a valid result or an error
if jsn_string is None or (
hasattr(jsn_string, "data") and jsn_string.data is None
):
# An error occurred, raise appropriate exception
err_code, err_desc = _get_error_string(error_code, error_description)
error_msg = "Failed to fetch session transcript"
if err_desc:
error_msg = error_msg + ": " + err_desc
raise _status_code_to_exception(err_code or error_code.value, error_msg)
# Successfully got the JSON string, parse it and free the C string
# The return value is wrapped in a String object by ctypes
# The String wrapper handles memory, so we don't need to manually free
json_str = str(jsn_string)
result = json.loads(json_str)
return result