Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/client/src/launchdarkly_ai_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
HandlerResult,
HandlerStreamEvent,
InitClientOptions,
InputTokenDetails,
JudgeResult,
JudgeRunResult,
JudgeTask,
Expand All @@ -56,16 +57,26 @@
)
from .types_validation import parse_ai_config
from .utils import (
RunUsage,
SpanUsage,
add_cached_tokens_to_input,
create_handler,
create_run_usage,
end_span_once,
lang_chain_span_usage,
make_track_data,
normalize_mode,
number_or_zero,
parse_json_with_possible_fences,
parse_template,
parse_usage,
set_ld_span_attributes,
set_model_identity_attributes,
set_openllmetry_completion,
set_openllmetry_prompt,
set_usage_span_attributes,
to_ld_context,
to_usage_dict,
)

__all__ = [ # noqa: RUF022
Expand Down Expand Up @@ -100,7 +111,18 @@
"StreamDoneEvent",
"StreamEvent",
"TrackData",
"InputTokenDetails",
"RunUsage",
"SpanUsage",
"UsageDict",
"add_cached_tokens_to_input",
"create_run_usage",
"end_span_once",
"lang_chain_span_usage",
"number_or_zero",
"set_model_identity_attributes",
"set_usage_span_attributes",
"to_usage_dict",
"VariationMeta",
# utils
"create_handler",
Expand Down
8 changes: 2 additions & 6 deletions packages/client/src/launchdarkly_ai_server/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
ProviderHandler,
ProviderResponse,
StreamEvent,
UsageDict,
VariationMeta,
)
from .utils import (
parse_json_with_possible_fences,
select_handler,
to_usage_dict,
)


Expand Down Expand Up @@ -112,11 +112,7 @@ async def invoke(
else json.dumps(parsed_response)
)

usage_obj = UsageDict(
input=usage.get("input", 0),
output=usage.get("output", 0),
total=usage.get("total", 0),
)
usage_obj = to_usage_dict(usage)

if self._skip_judges:
judge_tasks = await build_judge_tasks(
Expand Down
10 changes: 9 additions & 1 deletion packages/client/src/launchdarkly_ai_server/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,15 @@ async def invoke(

return ProviderGraphResponse(
response=final_response,
usage=UsageDict(**total_usage),
# Named rather than splatted, so a new UsageDict member cannot silently arrive
# here from a dict that has no business filling it. Graph totals carry no cache
# breakdown: they are a sum across nodes, and the per-node detail is on the node's
# own spans.
usage=UsageDict(
input=total_usage["input"],
output=total_usage["output"],
total=total_usage["total"],
),
judge_results=judge_results,
)

Expand Down
8 changes: 2 additions & 6 deletions packages/client/src/launchdarkly_ai_server/judges.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
NativeTool,
ProviderHandler,
TrackData,
UsageDict,
)
from .utils import (
collapse_messages_to_instructions as _collapse_messages_to_instructions,
Expand All @@ -22,6 +21,7 @@
normalize_mode,
parse_json_with_possible_fences,
to_ld_context,
to_usage_dict,
)


Expand Down Expand Up @@ -409,11 +409,7 @@ def _matches(h: ProviderHandler) -> bool:
reasoning = parsed.get("reasoning", "")
raw_usage = result["usage"]

usage = UsageDict(
input=raw_usage.get("input", 0),
output=raw_usage.get("output", 0),
total=raw_usage.get("total", 0),
)
usage = to_usage_dict(raw_usage)

merged_track_data: TrackData = {
**task.parent_track_data,
Expand Down
15 changes: 15 additions & 0 deletions packages/client/src/launchdarkly_ai_server/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,26 @@ def has_stream(self) -> bool:
# ---------------------------------------------------------------------------


@dataclass
class InputTokenDetails:
"""The cache breakdown behind an inclusive ``input`` figure.

Present only when the provider reported at least one cache field. ``uncached + cache_read +
cache_creation`` equals :attr:`UsageDict.input`.
"""

uncached: int = 0
cache_read: int = 0
cache_creation: int = 0


@dataclass
class UsageDict:
input: int = 0
output: int = 0
total: int = 0
#: Cache breakdown, when the provider reported one. See :class:`InputTokenDetails`.
input_details: InputTokenDetails | None = None
Comment thread
cursor[bot] marked this conversation as resolved.


@dataclass
Expand Down
Loading
Loading