ctfy.sdk.admin_resources.llm_calls

client.admin.llm_calls — every model request the gateway relayed.

 1"""``client.admin.llm_calls`` — every model request the gateway relayed."""
 2
 3from __future__ import annotations
 4
 5from typing import Any
 6
 7from ctfy.core.enums import PrincipalOrAny
 8from ctfy.sdk._helpers import _raise_for_status
 9from ctfy.sdk.base import BaseHttpClient
10from ctfy.server.models import LlmCallBody, LlmCallPage
11
12
13class AdminLlmCallsResource:
14    """The operator's whole transcript feed.
15
16    ⚠️ **The only surface on which a ``principal == "instance"`` row can
17    be read at all**, and admin-gated rather than organiser-gated for
18    that reason: an ``AGENTBENCH`` container's conversation carries the
19    challenge's minted answer, its system prompt and the hidden state of
20    a prompt-injection task — facts about a benchmark that outlives any
21    one competition.
22
23    ⚠️ **``indexing`` rides the envelope.** An empty list means *either*
24    that nothing has been relayed *or* that this deployment runs no
25    gateway, and those call for opposite actions. Same shape as
26    ``gateway_configured`` on the budget page.
27    """
28
29    def __init__(self, http: BaseHttpClient) -> None:
30        self._http = http
31
32    def list(
33        self,
34        *,
35        principal: PrincipalOrAny = "",
36        competition_id: str = "",
37        team_id: str = "",
38        challenge_id: str = "",
39        instance_id: str = "",
40        model: str = "",
41        status: int = 0,
42        since_ts: float = 0.0,
43        offset: int = 0,
44        limit: int = 50,
45    ) -> LlmCallPage:
46        """Relayed calls, newest first.
47
48        ``principal`` is the first control: ``instance`` (a challenge
49        container), ``agent`` (somebody's own eval-run agent) or
50        ``platform`` (the report grader, the AWD attacker).
51        ⚠️ ``total`` is counted with the **same** filters as the page, or
52        the paginator offers a page three of two.
53        Wraps ``GET /admin/llm-calls``."""
54        params: dict[str, Any] = {
55            "principal": principal,
56            "competition_id": competition_id,
57            "team_id": team_id,
58            "challenge_id": challenge_id,
59            "instance_id": instance_id,
60            "model": model,
61            "status": status,
62            "since_ts": since_ts,
63            "offset": offset,
64            "limit": limit,
65        }
66        resp = self._http.request("GET", "/admin/llm-calls", params=params)
67        _raise_for_status(resp)
68        return LlmCallPage.model_validate(resp.json())
69
70    def read(self, call_id: str) -> LlmCallBody:
71        """The archived conversation for one call.
72
73        ⚠️ ``available=False`` is an ordinary answer rather than an
74        error: retention reclaims the object and leaves the row, so "the
75        call happened and its body is gone" is a state the page must be
76        able to render — a 404 would read as "no such call".
77        Wraps ``GET /admin/llm-calls/{call_id}``."""
78        resp = self._http.request("GET", f"/admin/llm-calls/{call_id}")
79        _raise_for_status(resp)
80        return LlmCallBody.model_validate(resp.json())
class AdminLlmCallsResource:
14class AdminLlmCallsResource:
15    """The operator's whole transcript feed.
16
17    ⚠️ **The only surface on which a ``principal == "instance"`` row can
18    be read at all**, and admin-gated rather than organiser-gated for
19    that reason: an ``AGENTBENCH`` container's conversation carries the
20    challenge's minted answer, its system prompt and the hidden state of
21    a prompt-injection task — facts about a benchmark that outlives any
22    one competition.
23
24    ⚠️ **``indexing`` rides the envelope.** An empty list means *either*
25    that nothing has been relayed *or* that this deployment runs no
26    gateway, and those call for opposite actions. Same shape as
27    ``gateway_configured`` on the budget page.
28    """
29
30    def __init__(self, http: BaseHttpClient) -> None:
31        self._http = http
32
33    def list(
34        self,
35        *,
36        principal: PrincipalOrAny = "",
37        competition_id: str = "",
38        team_id: str = "",
39        challenge_id: str = "",
40        instance_id: str = "",
41        model: str = "",
42        status: int = 0,
43        since_ts: float = 0.0,
44        offset: int = 0,
45        limit: int = 50,
46    ) -> LlmCallPage:
47        """Relayed calls, newest first.
48
49        ``principal`` is the first control: ``instance`` (a challenge
50        container), ``agent`` (somebody's own eval-run agent) or
51        ``platform`` (the report grader, the AWD attacker).
52        ⚠️ ``total`` is counted with the **same** filters as the page, or
53        the paginator offers a page three of two.
54        Wraps ``GET /admin/llm-calls``."""
55        params: dict[str, Any] = {
56            "principal": principal,
57            "competition_id": competition_id,
58            "team_id": team_id,
59            "challenge_id": challenge_id,
60            "instance_id": instance_id,
61            "model": model,
62            "status": status,
63            "since_ts": since_ts,
64            "offset": offset,
65            "limit": limit,
66        }
67        resp = self._http.request("GET", "/admin/llm-calls", params=params)
68        _raise_for_status(resp)
69        return LlmCallPage.model_validate(resp.json())
70
71    def read(self, call_id: str) -> LlmCallBody:
72        """The archived conversation for one call.
73
74        ⚠️ ``available=False`` is an ordinary answer rather than an
75        error: retention reclaims the object and leaves the row, so "the
76        call happened and its body is gone" is a state the page must be
77        able to render — a 404 would read as "no such call".
78        Wraps ``GET /admin/llm-calls/{call_id}``."""
79        resp = self._http.request("GET", f"/admin/llm-calls/{call_id}")
80        _raise_for_status(resp)
81        return LlmCallBody.model_validate(resp.json())

The operator's whole transcript feed.

⚠️ The only surface on which a principal == "instance" row can be read at all, and admin-gated rather than organiser-gated for that reason: an AGENTBENCH container's conversation carries the challenge's minted answer, its system prompt and the hidden state of a prompt-injection task — facts about a benchmark that outlives any one competition.

⚠️ indexing rides the envelope. An empty list means either that nothing has been relayed or that this deployment runs no gateway, and those call for opposite actions. Same shape as gateway_configured on the budget page.

AdminLlmCallsResource(http: ctfy.sdk.base.BaseHttpClient)
30    def __init__(self, http: BaseHttpClient) -> None:
31        self._http = http
def list( self, *, principal: Union[ctfy.core.enums.Principal, Literal['']] = '', competition_id: str = '', team_id: str = '', challenge_id: str = '', instance_id: str = '', model: str = '', status: int = 0, since_ts: float = 0.0, offset: int = 0, limit: int = 50) -> ctfy.server.models.LlmCallPage:
33    def list(
34        self,
35        *,
36        principal: PrincipalOrAny = "",
37        competition_id: str = "",
38        team_id: str = "",
39        challenge_id: str = "",
40        instance_id: str = "",
41        model: str = "",
42        status: int = 0,
43        since_ts: float = 0.0,
44        offset: int = 0,
45        limit: int = 50,
46    ) -> LlmCallPage:
47        """Relayed calls, newest first.
48
49        ``principal`` is the first control: ``instance`` (a challenge
50        container), ``agent`` (somebody's own eval-run agent) or
51        ``platform`` (the report grader, the AWD attacker).
52        ⚠️ ``total`` is counted with the **same** filters as the page, or
53        the paginator offers a page three of two.
54        Wraps ``GET /admin/llm-calls``."""
55        params: dict[str, Any] = {
56            "principal": principal,
57            "competition_id": competition_id,
58            "team_id": team_id,
59            "challenge_id": challenge_id,
60            "instance_id": instance_id,
61            "model": model,
62            "status": status,
63            "since_ts": since_ts,
64            "offset": offset,
65            "limit": limit,
66        }
67        resp = self._http.request("GET", "/admin/llm-calls", params=params)
68        _raise_for_status(resp)
69        return LlmCallPage.model_validate(resp.json())

Relayed calls, newest first.

principal is the first control: instance (a challenge container), agent (somebody's own eval-run agent) or platform (the report grader, the AWD attacker). ⚠️ total is counted with the same filters as the page, or the paginator offers a page three of two. Wraps GET /admin/llm-calls.

def read(self, call_id: str) -> ctfy.server.models.LlmCallBody:
71    def read(self, call_id: str) -> LlmCallBody:
72        """The archived conversation for one call.
73
74        ⚠️ ``available=False`` is an ordinary answer rather than an
75        error: retention reclaims the object and leaves the row, so "the
76        call happened and its body is gone" is a state the page must be
77        able to render — a 404 would read as "no such call".
78        Wraps ``GET /admin/llm-calls/{call_id}``."""
79        resp = self._http.request("GET", f"/admin/llm-calls/{call_id}")
80        _raise_for_status(resp)
81        return LlmCallBody.model_validate(resp.json())

The archived conversation for one call.

⚠️ available=False is an ordinary answer rather than an error: retention reclaims the object and leaves the row, so "the call happened and its body is gone" is a state the page must be able to render — a 404 would read as "no such call". Wraps GET /admin/llm-calls/{call_id}.