Skip to content
Open
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
42 changes: 42 additions & 0 deletions scripts/_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
VENDOR_DIR = os.path.join(_SKILL_DIR, ".vendor", _PY_TAG)
LOCKFILE = os.path.join(_SKILL_DIR, "requirements.lock")
DEFAULT_HOST = "https://mainnet.zklighter.elliot.ai"
AGENT_KIT_VERSION = "0.1.0"
_CREDENTIALS = None

# Keys whose values must never appear in logs, tracebacks, or agent output.
Expand Down Expand Up @@ -280,3 +281,44 @@ def ensure_lighter():
)
)
sys.exit(1)


def _resolve_sdk_version():
"""Return the installed `lighter-sdk` distribution version, falling back to
the in-module `lighter.__version__` (which can lag the package metadata)
and finally `unknown`. Pure resolution; never raises.
"""
try:
from importlib.metadata import version, PackageNotFoundError
except ImportError:
from importlib_metadata import version, PackageNotFoundError # type: ignore[no-redef]
try:
return version("lighter-sdk")
except PackageNotFoundError:
pass
try:
import lighter
return getattr(lighter, "__version__", "unknown") or "unknown"
except Exception:
return "unknown"


def user_agent():
"""`User-Agent` value identifying agent-kit traffic on the server side.

Format follows RFC 7231: ``lighter-agent-kit/<version> lighter-python/<sdk>``.
Computed lazily because resolving the SDK version requires the vendored
install to be on `sys.path`, which only happens after `ensure_lighter()`.
"""
return f"lighter-agent-kit/{AGENT_KIT_VERSION} lighter-python/{_resolve_sdk_version()}"


def tag_api_client(api_client):
"""Brand outgoing HTTP requests so the server can attribute traffic to the
agent kit. Idempotent — safe to apply more than once.

Pass the `lighter.ApiClient` instance directly, or `signer.api_client` for
a `SignerClient`. Returns the same client for fluent use.
"""
api_client.user_agent = user_agent()
return api_client
4 changes: 3 additions & 1 deletion scripts/paper.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _cli import JsonArgumentParser, error, output # noqa: E402
from _paths import paper_state_path # noqa: E402
from _sdk import DEFAULT_HOST, ensure_lighter, get_config_value # noqa: E402
from _sdk import DEFAULT_HOST, ensure_lighter, get_config_value, tag_api_client # noqa: E402
from _symbols import normalize_side, resolve_symbol # noqa: E402

ensure_lighter()
Expand Down Expand Up @@ -375,6 +375,7 @@ async def _run_read(operation, *, refresh=True):
async with lighter.ApiClient(
configuration=lighter.Configuration(host=host),
) as api_client:
tag_api_client(api_client)
paper = _hydrate_paper_client(api_client, tier_enum, account, configs)
if refresh:
_, failures = await _refresh_position_markets(paper)
Expand All @@ -397,6 +398,7 @@ async def _run_with_paper_market(symbol, operation=None):
async with lighter.ApiClient(
configuration=lighter.Configuration(host=host),
) as api_client:
tag_api_client(api_client)
try:
market_id, market_type, _ = await resolve_symbol(
symbol,
Expand Down
3 changes: 3 additions & 0 deletions scripts/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ensure_lighter,
get_config_value,
resolve_with_source,
tag_api_client,
)
from _symbols import resolve_symbol # noqa: E402

Expand Down Expand Up @@ -402,6 +403,7 @@ async def get_auth_token(host):
account_index=account_index,
api_private_keys={api_key_index: api_private_key.expose()},
)
tag_api_client(signer.api_client)
except Exception as e:
error(f"failed to initialize signer for auth token: {e}")
try:
Expand Down Expand Up @@ -487,6 +489,7 @@ async def run(args):

try:
async with lighter.ApiClient(configuration=config) as client:
tag_api_client(client)
group = args.group
action = args.action

Expand Down
3 changes: 2 additions & 1 deletion scripts/trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _cli import JsonArgumentParser, error, output # noqa: E402
from _sdk import DEFAULT_HOST, ensure_lighter, get_config_value # noqa: E402
from _sdk import DEFAULT_HOST, ensure_lighter, get_config_value, tag_api_client # noqa: E402
from _symbols import normalize_side, resolve_symbol, side_to_is_ask # noqa: E402

ensure_lighter()
Expand Down Expand Up @@ -323,6 +323,7 @@ async def build_signer_client():
account_index=account_index,
api_private_keys={api_key_index: api_private_key.expose()},
)
tag_api_client(client.api_client)
except Exception as e:
error(f"failed to initialize signer: {clean_sdk_error(str(e))}")

Expand Down
1 change: 1 addition & 0 deletions tests/test_trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class JsonArgumentParser: # pragma: no cover - import stub only
sdk_mod.DEFAULT_HOST = "https://mainnet.zklighter.elliot.ai"
sdk_mod.ensure_lighter = lambda: None
sdk_mod.get_config_value = lambda *args, **kwargs: None
sdk_mod.tag_api_client = lambda api_client: api_client

symbols_mod = types.ModuleType("_symbols")
symbols_mod.normalize_side = lambda side, market_type: side
Expand Down
1 change: 1 addition & 0 deletions tests/test_trade_close_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class JsonArgumentParser: # pragma: no cover - import stub only
sdk_mod.DEFAULT_HOST = "https://mainnet.zklighter.elliot.ai"
sdk_mod.ensure_lighter = lambda: None
sdk_mod.get_config_value = lambda *args, **kwargs: None
sdk_mod.tag_api_client = lambda api_client: api_client

symbols_mod = types.ModuleType("_symbols")
symbols_mod.normalize_side = lambda side, market_type: side
Expand Down