Skip to content

Base

base

Base classes used across the different LLM endpoint types offered by LLMeter

You can also use these classes to implement your own custom Endpoint integrations.

Endpoint

Endpoint(endpoint_name, model_id, provider)

Bases: Serializable, ABC, Generic[TRawResponse]

An abstract base class for endpoint implementations.

We strongly recommend using the llmeter_invoke decorator to implement custom endpoints as shown below - which wraps payload pre-processing, response parsing, and error handling around a core invoke function you provide.

Example
class MyCustomEndpoint(Endpoint[MyAISDKRawReturnType]):
    @Endpoint.llmeter_invoke
    def invoke(self, payload: dict) -> MyAISDKRawReturnType:
        # Just the raw AI / SDK call goes here:
        raw: MyAISDKRawReturnType = self._my_cool_api_client.call(**payload)
        return raw

    def process_raw_response(
        self,
        raw_response: MyAISDKRawReturnType,
        start_t: float,
        response: InvocationResponse
    ):
        # llmeter_invoke wrapper automatically calls process_raw_response,
        # in which you should parse the outputs onto `response`
        response.id = raw_response["ResponseId"]
        ...

See llmeter_invoke and process_raw_responsefor more info.

You can also implement:

  • create_payload convenience method to simplify building payload objects for your endpoint - for example converting a simple input prompt to a full request object with other required parameters.
  • prepare_payload in case you need to do any request payload pre-processing outside the timer that measures response speed

Parameters:

Name Type Description Default
endpoint_name str

The name of the endpoint.

required
model_id str

The identifier of the model associated with this endpoint.

required
provider str

The provider of the endpoint.

required
Source code in llmeter/endpoints/base.py
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
@abstractmethod
def __init__(
    self,
    endpoint_name: str,
    model_id: str,
    provider: str,
):
    """
    Initialize the BaseEndpoint.

    Args:
        endpoint_name (str): The name of the endpoint.
        model_id (str): The identifier of the model associated with this endpoint.
        provider (str): The provider of the endpoint.
    """
    self.endpoint_name = endpoint_name
    self.model_id = model_id
    self.provider = provider

__subclasshook__ classmethod

__subclasshook__(C)

Determine if a class is considered a subclass of BaseEndpoint.

This method is used to implement a custom subclass check. A class is considered a subclass of BaseEndpoint if it has an 'invoke' method.

Parameters:

Name Type Description Default
C type

The class to check.

required

Returns:

Type Description
bool

bool or NotImplemented: True if the class is a subclass, False if it isn't, or NotImplemented if the check is inconclusive.

Source code in llmeter/endpoints/base.py
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
@classmethod
def __subclasshook__(cls, C: type) -> bool:
    """
    Determine if a class is considered a subclass of BaseEndpoint.

    This method is used to implement a custom subclass check. A class
    is considered a subclass of BaseEndpoint if it has an 'invoke' method.

    Args:
        C: The class to check.

    Returns:
        bool or NotImplemented: True if the class is a subclass, False if it isn't,
                                or NotImplemented if the check is inconclusive.
    """
    if cls is Endpoint:
        if any("invoke" in B.__dict__ for B in C.__mro__):
            return True
    return NotImplemented

create_payload staticmethod

create_payload(*args, **kwargs)

Create a payload for the endpoint invocation.

This static method should be implemented by subclasses to define how the payload is created based on the given arguments. Ideally, subclasses should conform to the conventions of existing endpoint types (for example taking a user_message: str | list[ContentItem] param), but this is not strictly enforced at the typing level.

Parameters:

Name Type Description Default
*args Any

Variable length argument list.

()
**kwargs Any

Arbitrary keyword arguments.

{}

Returns:

Name Type Description
NotImplemented Any

This method returns NotImplemented in the base class.

Source code in llmeter/endpoints/base.py
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
@staticmethod
def create_payload(*args: Any, **kwargs: Any) -> Any:
    """
    Create a payload for the endpoint invocation.

    This static method should be implemented by subclasses to define
    how the payload is created based on the given arguments. Ideally,
    subclasses should conform to the conventions of existing endpoint types
    (for example taking a `user_message: str | list[ContentItem]` param),
    but this is not strictly enforced at the typing level.

    Args:
        *args: Variable length argument list.
        **kwargs: Arbitrary keyword arguments.

    Returns:
        NotImplemented: This method returns NotImplemented in the base class.
    """
    return NotImplemented

invoke abstractmethod

invoke(payload)

Call a model and return a full parsed response with error handling

Info

We strongly encourage to use the llmeter_invoke decorator to implement your invoke method with proper orchestration and error handling.

Endpoint.invoke should:

  1. Call prepare_payload to transform the input payload
  2. Invoke your actual target endpoint
  3. Parse the results onto an InvocationResponse object (preferably via process_raw_response)
  4. Populate .error and as many other response fields as possible, in the event that an error occurs during model calling or response processing

The llmeter_invoke decorator handles this flow for you - so you'll need to re-implement the steps if you choose not to use it.

Parameters:

Name Type Description Default
payload dict

The input payload for the model.

required

Returns:

Name Type Description
response InvocationResponse

The final InvocationResponse, including all the information that could be parsed from the API response - even in case of an error (when the error field should also be set)

Source code in llmeter/endpoints/base.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
@abstractmethod
def invoke(self, payload: dict) -> InvocationResponse:
    """Call a model and return a full parsed response with error handling

    !!! info
        We strongly encourage to use the
        [`llmeter_invoke`](llmeter.endpoints.base.Endpoint.llmeter_invoke) decorator to implement
        your invoke method with proper orchestration and error handling.

    `Endpoint.invoke` should:

    1. Call `prepare_payload` to transform the input payload
    2. Invoke your actual target endpoint
    3. Parse the results onto an
        [`InvocationResponse`](llmeter.endpoints.base.InvocationResponse) object (preferably
        via [`process_raw_response`](llmeter.endpoints.base.Endpoint.process_raw_response))
    4. Populate `.error` and as many other response fields as possible, in the event that an
        error occurs during model calling or response processing

    The `llmeter_invoke` decorator handles this flow for you - so you'll need to re-implement
    the steps if you choose not to use it.

    Args:
        payload: The input payload for the model.

    Returns:
        response: The final `InvocationResponse`, including all the information that could be
            parsed from the API response - even in case of an error (when the ``error`` field
            should also be set)
    """
    raise NotImplementedError

llmeter_invoke classmethod

llmeter_invoke(call_endpoint)

Wrap a raw model API call with pre+postprocessing and error handling

This decorator wraps around a function that only does the core model call, to add the full range of steps that LLMeter Endpoints are expected to handle as part of invoke:

  1. Before starting the response timer, calls your class' prepare_payload method to transform the input payload, if required
  2. Initialises an InvocationResponse with the timestamp of the request.
  3. Calls the wrapped function to fetch the raw API response
  4. Calls your class' process_raw_response method to incrementally parse fields from the raw response to the target InvocationResponse
  5. In case of any unhandled errors during API call or response processing, logs and sets response.error
  6. Automatically backfills the following fields on the parsed response, if missing:
    • id (as a generated UUID)
    • input_payload (the final payload sent to the API)
    • input_prompt (via _parse_payload method)
    • time_to_last_token

Parameters:

Name Type Description Default
call_endpoint Callable[..., TRawResponse]

The function to wrap. Should be a method that takes a payload: dict and returns a raw_response object for input to process_raw_response

required

Returns:

Type Description
Callable[..., InvocationResponse]

A wrapped function that implements the full invoke logic.

Source code in llmeter/endpoints/base.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@classmethod
def llmeter_invoke(
    cls,
    call_endpoint: Callable[..., TRawResponse],
) -> Callable[..., InvocationResponse]:
    """Wrap a raw model API call with pre+postprocessing and error handling

    This decorator wraps around a function that *only* does the core model call, to add the
    full range of steps that LLMeter Endpoints are expected to handle as part of `invoke`:

    1. **Before** starting the response timer, calls your class'
        [`prepare_payload`](llmeter.endpoints.base.Endpoint.prepare_payload) method to
        transform the input payload, if required
    2. Initialises an [`InvocationResponse`](llmeter.endpoints.base.InvocationResponse) with
        the timestamp of the request.
    3. Calls the wrapped function to fetch the raw API response
    4. Calls your class'
        [`process_raw_response`](llmeter.endpoints.base.Endpoint.process_raw_response) method
        to incrementally parse fields from the raw response to the target `InvocationResponse`
    5. In case of any unhandled errors during API call or response processing, logs and sets
        `response.error`
    6. Automatically backfills the following fields on the parsed response, if missing:
        - `id` (as a generated UUID)
        - `input_payload` (the final payload sent to the API)
        - `input_prompt` (via
            [`_parse_payload`](llmeter.endpoints.base.Endpoint._parse_payload) method)
        - `time_to_last_token`

    Args:
        call_endpoint: The function to wrap. Should be a method that takes a `payload: dict`
            and returns a `raw_response` object for input to `process_raw_response`

    Returns:
        A wrapped function that implements the full `invoke` logic.
    """

    @functools.wraps(call_endpoint)
    def wrapper(self: "Endpoint", payload: dict) -> InvocationResponse:
        prepared = self.prepare_payload(payload)
        # Snapshot before the API call for _parse_payload, which runs after
        # the inner invoke — by which point the client may have mutated the dict.
        saved_payload = copy.deepcopy(prepared)
        default_response_id = uuid4().hex
        response = InvocationResponse(
            id=default_response_id,
            request_time=datetime.now(timezone.utc),
            response_text=None,
        )
        start_t = time.perf_counter()
        try:
            raw_response: TRawResponse = call_endpoint(self, prepared)
            self.process_raw_response(raw_response, start_t, response)
            default_end_t = time.perf_counter()
        except Exception as e:
            default_end_t = time.perf_counter()
            logger.exception("Endpoint invocation failed: %s", response.error or e)
            if not response.error:
                response.error = str(e)

        if response.id is None:
            # Just in case user's parsing logic accidentally cleared the default ID provided:
            response.id = default_response_id

        if response.time_to_last_token is None and response.error is None:
            response.time_to_last_token = default_end_t - start_t

        if response.input_payload is None:
            response.input_payload = prepared
        if response.input_prompt is None:
            try:
                response.input_prompt = self._parse_payload(saved_payload)
            except Exception:
                logger.debug("_parse_payload failed; leaving input_prompt as None")

        return response

    # Add a private marker to indicate that the wrapping happened:
    # (We don't currently use this for anything except unit tests)
    wrapper._is_llmeter_invoke = True  # type: ignore
    return wrapper

load classmethod

load(endpoint_config)

Load an endpoint configuration from a dictionary.

This class method reads a dictionary containing an endpoint configuration, determines the appropriate endpoint class, and instantiates it with the loaded configuration.

Deprecated

This supports the legacy {"endpoint_type": ...} format. New code should use load_object with dicts produced by dump_object.

Parameters:

Name Type Description Default
endpoint_config dict

A dictionary containing the endpoint configuration. Must include at minimum an endpoint_type key.

required

Returns:

Name Type Description
Endpoint Endpoint

An instance of the appropriate endpoint class, initialized with the configuration from the dictionary.

Source code in llmeter/endpoints/base.py
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
@classmethod
def load(cls, endpoint_config: dict) -> "Endpoint":  # type: ignore
    """Load an endpoint configuration from a dictionary.

    This class method reads a dictionary containing an endpoint configuration,
    determines the appropriate endpoint class, and instantiates it with the
    loaded configuration.

    !!! warning "Deprecated"
        This supports the legacy `{"endpoint_type": ...}` format. New code should
        use [`load_object`][llmeter.serialization.load_object] with dicts produced by
        [`dump_object`][llmeter.serialization.dump_object].

    Args:
        endpoint_config (dict): A dictionary containing the endpoint configuration.
            Must include at minimum an `endpoint_type` key.

    Returns:
        Endpoint: An instance of the appropriate endpoint class, initialized
                  with the configuration from the dictionary.
    """
    endpoint_type = endpoint_config.pop("endpoint_type")
    endpoint_module = importlib.import_module("llmeter.endpoints")
    endpoint_class = getattr(endpoint_module, endpoint_type)
    return endpoint_class(
        **_filter_legacy_ctor_kwargs(endpoint_class, endpoint_config)
    )

load_from_file classmethod

load_from_file(path)

Load an endpoint configuration from a JSON file.

This class method reads a JSON file containing an endpoint configuration, determines the appropriate endpoint class, and instantiates it with the loaded configuration.

Parameters:

Name Type Description Default
path str | UPath

The path to the JSON configuration file.

required

Returns:

Name Type Description
Endpoint Endpoint

An instance of the appropriate endpoint class, initialized with the configuration from the file.

Source code in llmeter/endpoints/base.py
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
@classmethod
def load_from_file(cls, path: ReadablePathLike) -> "Endpoint":
    """Load an endpoint configuration from a JSON file.

    This class method reads a JSON file containing an endpoint configuration,
    determines the appropriate endpoint class, and instantiates it with the
    loaded configuration.

    Args:
        path (str | UPath): The path to the JSON configuration file.

    Returns:
        Endpoint: An instance of the appropriate endpoint class, initialized
                  with the configuration from the file.
    """
    path = ensure_path(path)
    with path.open("r") as f:
        data = json.load(f)
    if "__llmeter_class__" in data:
        return load_object(data)
    endpoint_type = data.pop("endpoint_type")
    endpoint_module = importlib.import_module("llmeter.endpoints")
    endpoint_class = getattr(endpoint_module, endpoint_type)
    return endpoint_class(**_filter_legacy_ctor_kwargs(endpoint_class, data))

prepare_payload

prepare_payload(payload)

Transform the payload before sending it to the API.

You can use it to enforce any transformations you need between the input dataset/payload and what actually gets sent to the model, that should not be counted in the response time measurement. For example: Setting fixed parameters required by the endpoint e.g. streaming: False.

This method is called by the llmeter_invoke wrapper before starting the timer that measures response latency.

Warning

If you made a custom :meth:invoke implementation without using the :meth:llmeter_invoke decorator - check whether your implementation actually calls this prepare_payload method or not!

The default implementation returns payload unchanged

Parameters:

Name Type Description Default
payload dict

The raw input payload from the caller.

required

Returns:

Name Type Description
dict dict

The final payload to send to the API.

Source code in llmeter/endpoints/base.py
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
def prepare_payload(self, payload: dict) -> dict:
    """Transform the payload before sending it to the API.

    You can use it to enforce any transformations you need between the input dataset/payload
    and what actually gets sent to the model, that should not be counted in the response time
    measurement. For example: Setting fixed parameters required by the endpoint e.g.
    `streaming: False`.

    This method is called by the
    [`llmeter_invoke`](llmeter.endpoints.base.Endpoint.llmeter_invoke) wrapper *before*
    starting the timer that measures response latency.

    !!! warning
        If you made a custom :meth:`invoke` implementation **without** using the
        :meth:`llmeter_invoke` decorator - check whether your implementation actually calls
        this `prepare_payload` method or not!

    The default implementation returns ``payload`` unchanged

    Args:
        payload: The raw input payload from the caller.

    Returns:
        dict: The final payload to send to the API.
    """
    return payload

process_raw_response abstractmethod

process_raw_response(raw_response, start_t, response)

Parse a raw API response onto InvocationResponse fields

Subclasses implement this to extract LLMeter data points (such as time to first and last token, output text, number of input/output tokens, etc.) from raw model responses.

Warning

If you made a custom :meth:invoke implementation without using the :meth:llmeter_invoke decorator - check whether your implementation actually calls this process_raw_response method or not!

This function does not return a value, but is instead expected to incrementally populate properties on the provided draft response object.

In this way, partial data will be stored even if an error occurs later during processing. For example if a stream times out, or a guardrail intervenes - we might still be able to capture a unique ID initially pulled from the response header.

See llmeter_invoke for more details about which fields of InvocationResponse are automatically populated for you.

Parameters:

Name Type Description Default
raw_response TRawResponse

The raw response object (returned by your invoke method before it's wrapped with llmeter_invoke)

required
start_t float

time.perf_counter timestamp captured immediately before the API call. Use this to calculate and populate response.time_to_last_token and (if in streaming mode) response.time_to_first_token.

required
response InvocationResponse

The LLMeter response object to be populated in-place.

required

Raises:

Type Description
Exception

If something goes wrong during response streaming or parsing, implementations can just raise an error. The :meth:llmeter_invoke wrapper will populate response.error and response.time_to_last_token if they're not set already.

Source code in llmeter/endpoints/base.py
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
@abstractmethod
def process_raw_response(
    self,
    raw_response: TRawResponse,
    start_t: float,
    response: InvocationResponse,
) -> None:
    """Parse a raw API response onto `InvocationResponse` fields

    Subclasses implement this to extract LLMeter data points (such as time to first and last
    token, output text, number of input/output tokens, etc.) from raw model responses.

    !!! warning
        If you made a custom :meth:`invoke` implementation **without** using the
        :meth:`llmeter_invoke` decorator - check whether your implementation actually calls
        this `process_raw_response` method or not!

    This function does not return a value, but is instead expected to incrementally populate
    properties on the provided draft ``response`` object.

    In this way, partial data will be stored even if an error occurs later during processing.
    For example if a stream times out, or a guardrail intervenes - we might still be able to
    capture a unique ID initially pulled from the response header.

    See [`llmeter_invoke`](llmeter.endpoints.base.Endpoint.llmeter_invoke) for more details
    about which fields of `InvocationResponse` are automatically populated for you.

    Args:
        raw_response: The raw response object (returned by your `invoke` method before it's
            wrapped with `llmeter_invoke`)
        start_t: `time.perf_counter` timestamp captured immediately before the API call.
            Use this to calculate and populate `response.time_to_last_token` and (if in
            streaming mode) `response.time_to_first_token`.
        response: The LLMeter response object to be populated **in-place**.

    Raises:
        Exception: If something goes wrong during response streaming or parsing,
            implementations can just raise an error. The :meth:`llmeter_invoke` wrapper will
            populate ``response.error`` and ``response.time_to_last_token`` if they're not set
            already.
    """
    raise NotImplementedError

save

save(output_path)

Save the endpoint configuration to a JSON file.

.. deprecated:: Use :meth:~llmeter.serialization.Serializable.save_to_file instead, which provides the same behavior with a consistent name across all serializable LLMeter objects. This alias will be removed in a future major version.

Parameters:

Name Type Description Default
output_path str | UPath

The path where the configuration file will be saved.

required

Returns:

Name Type Description
Path UPath

The path the file was written to.

Source code in llmeter/endpoints/base.py
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
def save(self, output_path: WritablePathLike) -> Path:
    """Save the endpoint configuration to a JSON file.

    .. deprecated::
        Use :meth:`~llmeter.serialization.Serializable.save_to_file` instead, which
        provides the same behavior with a consistent name across all serializable
        LLMeter objects. This alias will be removed in a future major version.

    Args:
        output_path (str | UPath): The path where the configuration file will be saved.

    Returns:
        Path: The path the file was written to.
    """
    warnings.warn(
        "Endpoint.save() is deprecated and will be removed in a future version; "
        "use Endpoint.save_to_file() instead.",
        DeprecationWarning,
        stacklevel=2,
    )
    return self.save_to_file(output_path)

to_dict

to_dict()

Convert the endpoint configuration to a dictionary.

Returns:

Name Type Description
Dict dict

A dictionary representation of the endpoint configuration.

Source code in llmeter/endpoints/base.py
530
531
532
533
534
535
536
537
538
539
def to_dict(self) -> dict:
    """
    Convert the endpoint configuration to a dictionary.

    Returns:
        Dict: A dictionary representation of the endpoint configuration.
    """
    endpoint_conf = {k: v for k, v in vars(self).items() if not k.startswith("_")}
    endpoint_conf["endpoint_type"] = self.__class__.__name__
    return endpoint_conf

InvocationResponse dataclass

InvocationResponse(response_text, input_payload=None, id=None, input_prompt=None, time_to_first_token=None, time_to_last_token=None, num_tokens_input=None, num_tokens_output=None, num_tokens_input_cached=None, num_tokens_output_reasoning=None, time_per_output_token=None, error=None, retries=None, request_time=None, annotations=dict())

A class representing a invocation result.

Attributes:

Name Type Description
response_text str

The invocation output.

id str

A unique identifier for the invocation.

time_to_last_token float

The time taken to generate the response in seconds.

time_to_first_token float

The time taken to receive the first token of the response in seconds.

num_tokens_output Optional[int]

The number of tokens in the response.

num_tokens_input Optional[int]

The number of tokens in the invocation payload.

num_tokens_input_cached int | None

The number of input tokens served from cache (prompt caching).

num_tokens_output_reasoning int | None

The number of output tokens used for internal reasoning (included in num_tokens_output). Populated when the provider reports a separate reasoning/thinking token count (e.g. OpenAI reasoning_tokens). None when the provider does not break out reasoning tokens — for example, Anthropic includes thinking tokens in output_tokens without a separate count.

input_prompt str

The input prompt used in the invocation.

time_per_output_token float

The average time taken to generate each token in the response.

error str

Any error that occurred during invocation.

request_time datetime | None

The wall-clock time when the request was sent.

annotations dict

Free-form extra data attached to this response, for example by callbacks. This is the preferred place for a Callback to store additional per-response fields (rather than setting arbitrary attributes on the response), because annotations is a declared field and therefore round-trips through to_json/from_json and disk persistence. On load, any unrecognized top-level keys (e.g. from older files or other LLMeter versions) are collected into annotations too.

from_json classmethod

from_json(json_str)

Deserialize a JSON string into an InvocationResponse.

This is the inverse of to_json. It correctly restores types that the default JSON round-trip would leave as strings or marker objects:

  • datetime-annotated fields are parsed from ISO-8601 strings back to Python datetime
  • bytes-typed fields and __llmeter_bytes__ markers in nested payloads are restored

For legacy compatability, any top-level keys that are not recognized fields are currently collected into annotations rather than being dropped or raising an error. This supports older files where CostModel callbacks wrote extra fields (such as cost_*) directly onto the response - but may be dropped in a future version.

Parameters:

Name Type Description Default
json_str str

A JSON string representation of an InvocationResponse (produced by to_json or similar).

required

Returns:

Name Type Description
InvocationResponse InvocationResponse

The deserialized response.

Example

A round-trip can be run as follows:

original = InvocationResponse(response_text="hi", ...)
restored = InvocationResponse.from_json(original.to_json())

Source code in llmeter/endpoints/base.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@classmethod
def from_json(cls, json_str: str) -> "InvocationResponse":
    """Deserialize a JSON string into an `InvocationResponse`.

    This is the inverse of [`to_json`][llmeter.endpoints.base.InvocationResponse.to_json]. It
    correctly restores types that the default JSON round-trip would leave as strings or marker
    objects:

    * `datetime`-annotated fields are parsed from ISO-8601 strings back to Python `datetime`
    * `bytes`-typed fields and `__llmeter_bytes__` markers in nested payloads are restored

    For legacy compatability, any top-level keys that are *not* recognized fields are currently
    collected into [`annotations`][llmeter.endpoints.base.InvocationResponse] rather than being
    dropped or raising an error. This supports older files where CostModel callbacks wrote
    extra fields (such as `cost_*`) directly onto the response - but may be dropped in a future
    version.

    Args:
        json_str: A JSON string representation of an InvocationResponse (produced by `to_json`
            or similar).

    Returns:
        InvocationResponse: The deserialized response.

    Example:
        A round-trip can be run as follows:
        ```python
        original = InvocationResponse(response_text="hi", ...)
        restored = InvocationResponse.from_json(original.to_json())
        ```
    """
    data = json.loads(json_str, object_hook=bytes_decoder)
    restore_dataclass_types(cls, data)
    # Route any unrecognized top-level keys into `annotations` rather than failing. This keeps
    # forward/backward compatibility: e.g. older files where callbacks wrote extra fields (like
    # `cost_*`) directly onto the response, or fields written by a different LLMeter version.
    known = {f.name for f in fields(cls)}
    extras = {k: data.pop(k) for k in list(data) if k not in known}
    if extras:
        data["annotations"] = {**extras, **(data.get("annotations") or {})}
        logger.debug(
            "Loaded %d unrecognized InvocationResponse field(s) into `annotations`: %s",
            len(extras),
            ", ".join(sorted(extras)),
        )
    return cls(**data)

to_dict

to_dict()

Return a dictionary representation of this response.

Returns a plain dict produced by dataclasses.asdict, preserving native Python types (e.g. datetime for request_time). This is suitable for programmatic access — for example RunningStats consumes this output and relies on datetime comparisons and arithmetic.

For JSON output, use to_json, (which delegates to json_default by default, for non-JSON-serializable data types).

Returns:

Name Type Description
dict dict

A dictionary of response fields with native Python types.

Source code in llmeter/endpoints/base.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def to_dict(self) -> dict:
    """Return a dictionary representation of this response.

    Returns a plain `dict` produced by `dataclasses.asdict`, preserving native Python types
    (e.g. `datetime` for `request_time`).  This is suitable for programmatic access — for
    example [`RunningStats`][llmeter.utils.RunningStats] consumes this output and relies on
    `datetime` comparisons and arithmetic.

    For JSON output, use [`to_json`][llmeter.endpoints.base.InvocationResponse.to_json], (which
    delegates to [`json_default`][llmeter.serialization.json_default]
    by default, for non-JSON-serializable data types).

    Returns:
        dict: A dictionary of response fields with native Python types.
    """
    return asdict(self)

to_json

to_json(default=json_default, **kwargs)

Serialize this response to a JSON string.

Uses json_default by default, which handles bytes, datetime, PathLike, and other common non-serializable types.

Parameters:

Name Type Description Default
default Callable[[Any], Any] | None

Fallback serializer passed to json.dumps.

json_default
**kwargs Any

Additional arguments passed to json.dumps (e.g., indent, sort_keys).

{}

Returns:

Name Type Description
str str

JSON representation of the response.

Source code in llmeter/endpoints/base.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def to_json(
    self, default: Callable[[Any], Any] | None = json_default, **kwargs: Any
) -> str:
    """Serialize this response to a JSON string.

    Uses [`json_default`][llmeter.serialization.json_default] by
    default, which handles `bytes`, `datetime`, `PathLike`, and other common non-serializable
    types.

    Args:
        default: Fallback serializer passed to `json.dumps`.
        **kwargs: Additional arguments passed to `json.dumps` (e.g., `indent`, `sort_keys`).

    Returns:
        str: JSON representation of the response.
    """
    return json.dumps(asdict(self), default=default, **kwargs)