logfire-api 4.40.0__tar.gz → 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {logfire_api-4.40.0 → logfire_api-5.0.0}/PKG-INFO +2 -2
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/__init__.py +2 -1
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/__init__.pyi +7 -1
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/auth.pyi +1 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/gateway.pyi +1 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/client.pyi +9 -2
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/collect_system_info.pyi +1 -1
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/config.pyi +23 -12
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/constants.pyi +1 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/otlp.pyi +7 -3
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/forwarding.pyi +20 -1
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/claude_agent_sdk.pyi +13 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/flask.pyi +0 -1
- logfire_api-5.0.0/logfire_api/_internal/integrations/google_genai.pyi +12 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/openai_agents.pyi +4 -3
- logfire_api-5.0.0/logfire_api/_internal/integrations/system_metrics.pyi +49 -0
- logfire_api-5.0.0/logfire_api/_internal/interactive.pyi +53 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/main.pyi +31 -27
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/stack_info.pyi +12 -4
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/utils.pyi +11 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/db_api.pyi +39 -12
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/experimental/api_client.pyi +6 -6
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/pydantic.pyi +1 -0
- logfire_api-5.0.0/logfire_api/query_client.pyi +184 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/sampling/_tail_sampling.pyi +4 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/abstract.pyi +6 -6
- {logfire_api-4.40.0 → logfire_api-5.0.0}/pyproject.toml +1 -1
- logfire_api-4.40.0/logfire_api/_internal/integrations/google_genai.pyi +0 -29
- logfire_api-4.40.0/logfire_api/_internal/integrations/system_metrics.pyi +0 -21
- logfire_api-4.40.0/logfire_api/experimental/forwarding.pyi +0 -25
- logfire_api-4.40.0/logfire_api/query_client.pyi +0 -3
- {logfire_api-4.40.0 → logfire_api-5.0.0}/.gitignore +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/README.md +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/ast_utils.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/async_.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/auth.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/auto_trace/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/auto_trace/import_hook.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/auto_trace/rewrite_ast.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/auto_trace/types.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/baggage.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/ai_tools.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/gateway_auth.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/prompt.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli/run.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/cli.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/config_params.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/db_statement_summary.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/console.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/dynamic_batch.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/logs.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/processor_wrapper.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/quiet_metrics.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/remove_pending.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/tail_sampling.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/exporters/wrapper.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/formatter.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/instrument.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/aiohttp_client.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/aiohttp_server.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/asgi.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/asyncpg.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/aws_lambda.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/celery.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/django.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/dspy.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/executors.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/fastapi.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/httpx.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/litellm.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/mcp.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/mysql.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/print.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/psycopg.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/pydantic_ai.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/pymongo.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/pytest.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/redis.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/requests.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/sqlalchemy.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/sqlite3.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/starlette.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/surrealdb.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/wsgi.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/json_encoder.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/json_formatter.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/json_schema.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/json_types.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/logs.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/metrics.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/scrubbing.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/server_response.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/tracer.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/ulid.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/cli.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/exceptions.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/experimental/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/experimental/annotations.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/experimental/datasets/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/experimental/query_client.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/aiohttp_client.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/flask.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/httpx.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/logging.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/loguru.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/psycopg.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/redis.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/sqlalchemy.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/structlog.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/integrations/wsgi.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/propagate.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/py.typed +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/sampling/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/types.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/__init__.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/_handlebars.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/composition.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/config.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/local.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/remote.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/template_validation.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/variables/variable.pyi +0 -0
- {logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/version.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: logfire-api
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: Shim for the Logfire SDK which does nothing unless Logfire is installed
|
|
5
5
|
Author-email: Pydantic Team <engineering@pydantic.dev>, Samuel Colvin <samuel@pydantic.dev>, Hasan Ramezani <hasan@pydantic.dev>, Adrian Garcia Badaracco <adrian@pydantic.dev>, David Montague <david@pydantic.dev>, Marcelo Trylesinski <marcelo@pydantic.dev>, David Hewitt <david.hewitt@pydantic.dev>, Alex Hall <alex@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -124,7 +124,8 @@ except ImportError:
|
|
|
124
124
|
def with_settings(self, *args, **kwargs) -> Logfire:
|
|
125
125
|
return self
|
|
126
126
|
|
|
127
|
-
def force_flush(self, *args, **kwargs) ->
|
|
127
|
+
def force_flush(self, *args, **kwargs) -> bool:
|
|
128
|
+
return True
|
|
128
129
|
|
|
129
130
|
def log_slow_async_callbacks(self, *args, **kwargs) -> None: # pragma: no cover
|
|
130
131
|
return nullcontext()
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from collections.abc import Sequence
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, NamedTuple
|
|
4
4
|
|
|
5
5
|
__all__ = ['main', 'logfire_info']
|
|
6
6
|
|
|
7
|
+
class SavedReadToken(NamedTuple):
|
|
8
|
+
token: str
|
|
9
|
+
base_url: str
|
|
10
|
+
organization: str
|
|
11
|
+
project_name: str
|
|
12
|
+
|
|
7
13
|
def logfire_info() -> str:
|
|
8
14
|
"""Show versions of logfire, OS and related packages."""
|
|
9
15
|
|
|
@@ -2,6 +2,7 @@ import argparse
|
|
|
2
2
|
from ...exceptions import LogfireConfigError as LogfireConfigError
|
|
3
3
|
from ..auth import DEFAULT_FILE as DEFAULT_FILE, UserTokenCollection as UserTokenCollection, poll_for_token as poll_for_token, request_device_code as request_device_code
|
|
4
4
|
from ..config import REGIONS as REGIONS
|
|
5
|
+
from ..interactive import is_non_interactive as is_non_interactive, require_answer as require_answer
|
|
5
6
|
|
|
6
7
|
def parse_auth(args: argparse.Namespace) -> None:
|
|
7
8
|
"""Authenticate with Logfire.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import httpx
|
|
3
|
+
from ..interactive import require_answer as require_answer
|
|
3
4
|
from .ai_tools import AiToolIntegration as AiToolIntegration, LOCAL_TOKEN_PLACEHOLDER as LOCAL_TOKEN_PLACEHOLDER, ai_tool_names as ai_tool_names, gateway_template_values as gateway_template_values, resolve_ai_tool as resolve_ai_tool
|
|
4
5
|
from .gateway_auth import CimdOAuthClient as CimdOAuthClient, GATEWAY_CIMD_PATH as GATEWAY_CIMD_PATH, GatewayAuth as GatewayAuth, GatewayError as GatewayError, OAuthSession as OAuthSession, discover_oauth_metadata as discover_oauth_metadata
|
|
5
6
|
from _typeshed import Incomplete
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from .auth import UserToken as UserToken, UserTokenCollection as UserTokenCollection
|
|
2
|
+
from .constants import HTTP_CONNECT_TIMEOUT as HTTP_CONNECT_TIMEOUT
|
|
2
3
|
from .server_response import ServerResponseCallback as ServerResponseCallback, install_logfire_response_hook as install_logfire_response_hook
|
|
3
4
|
from .utils import UnexpectedResponse as UnexpectedResponse
|
|
4
5
|
from _typeshed import Incomplete
|
|
6
|
+
from datetime import datetime
|
|
5
7
|
from logfire.exceptions import LogfireConfigError as LogfireConfigError
|
|
6
8
|
from logfire.version import VERSION as VERSION
|
|
7
9
|
from typing import Any
|
|
@@ -55,7 +57,12 @@ class LogfireClient:
|
|
|
55
57
|
"""
|
|
56
58
|
def create_write_token(self, organization: str, project_name: str) -> dict[str, Any]:
|
|
57
59
|
"""Create a write token for the given project in the given organization."""
|
|
58
|
-
def create_read_token(self, organization: str, project_name: str) -> dict[str, Any]:
|
|
59
|
-
"""Create a read token for the given project in the given organization.
|
|
60
|
+
def create_read_token(self, organization: str, project_name: str, expires_at: datetime | None = None) -> dict[str, Any]:
|
|
61
|
+
"""Create a read token for the given project in the given organization.
|
|
62
|
+
|
|
63
|
+
`expires_at` bounds how long the token stays valid. The CLI has no way to revoke
|
|
64
|
+
one, so a token it stores on disk gets an expiry; a token it prints for the caller
|
|
65
|
+
to place somewhere else does not, because we do not know what it was used for.
|
|
66
|
+
"""
|
|
60
67
|
def get_prompt(self, organization: str, project_name: str, issue: str) -> dict[str, Any]:
|
|
61
68
|
"""Get a prompt to be used with your favorite LLM."""
|
|
@@ -16,6 +16,7 @@ from .exporters.remove_pending import RemovePendingSpansExporter as RemovePendin
|
|
|
16
16
|
from .exporters.test import TestExporter as TestExporter
|
|
17
17
|
from .forwarding import OTLPForwardingManager as OTLPForwardingManager
|
|
18
18
|
from .integrations.executors import instrument_executors as instrument_executors
|
|
19
|
+
from .interactive import ask_or_default as ask_or_default, ask_required as ask_required, require_answer as require_answer
|
|
19
20
|
from .logs import ProxyLoggerProvider as ProxyLoggerProvider
|
|
20
21
|
from .main import Logfire as Logfire
|
|
21
22
|
from .metrics import ProxyMeterProvider as ProxyMeterProvider
|
|
@@ -23,7 +24,7 @@ from .scrubbing import BaseScrubber as BaseScrubber, NOOP_SCRUBBER as NOOP_SCRUB
|
|
|
23
24
|
from .server_response import ServerResponseCallback as ServerResponseCallback, install_logfire_response_hook as install_logfire_response_hook
|
|
24
25
|
from .stack_info import warn_at_user_stacklevel as warn_at_user_stacklevel
|
|
25
26
|
from .tracer import OPEN_SPANS as OPEN_SPANS, PendingSpanProcessor as PendingSpanProcessor, ProxyTracerProvider as ProxyTracerProvider
|
|
26
|
-
from .utils import SeededRandomIdGenerator as SeededRandomIdGenerator, ensure_data_dir_exists as ensure_data_dir_exists, handle_internal_errors as handle_internal_errors, platform_is_emscripten as platform_is_emscripten, suppress_instrumentation as suppress_instrumentation
|
|
27
|
+
from .utils import SeededRandomIdGenerator as SeededRandomIdGenerator, ensure_data_dir_exists as ensure_data_dir_exists, handle_internal_errors as handle_internal_errors, platform_is_aws_lambda as platform_is_aws_lambda, platform_is_emscripten as platform_is_emscripten, suppress_instrumentation as suppress_instrumentation
|
|
27
28
|
from _typeshed import Incomplete
|
|
28
29
|
from collections.abc import Callable, Mapping, Sequence
|
|
29
30
|
from dataclasses import dataclass, field
|
|
@@ -45,8 +46,8 @@ from opentelemetry.sdk.resources import ResourceDetector
|
|
|
45
46
|
from opentelemetry.sdk.trace import SpanProcessor
|
|
46
47
|
from opentelemetry.sdk.trace.id_generator import IdGenerator
|
|
47
48
|
from pathlib import Path
|
|
48
|
-
from typing import Any, ClassVar, Literal, TextIO
|
|
49
|
-
from typing_extensions import Self
|
|
49
|
+
from typing import Any, ClassVar, Literal, TextIO
|
|
50
|
+
from typing_extensions import Self
|
|
50
51
|
|
|
51
52
|
CREDENTIALS_FILENAME: str
|
|
52
53
|
COMMON_REQUEST_HEADERS: Incomplete
|
|
@@ -76,7 +77,12 @@ class AdvancedOptions:
|
|
|
76
77
|
emit_configuration_span: bool | None = ...
|
|
77
78
|
server_response_hook: ServerResponseCallback | None = ...
|
|
78
79
|
resource_detectors: Sequence[ResourceDetector | str] | None = ...
|
|
79
|
-
def generate_base_url(self, token: str) -> str:
|
|
80
|
+
def generate_base_url(self, token: str, warn_unknown_region: bool = True) -> str:
|
|
81
|
+
"""Resolve the base API URL for `token`, honouring an explicitly configured `base_url`.
|
|
82
|
+
|
|
83
|
+
Unknown regions warn by default. The background credential check opts out because the
|
|
84
|
+
synchronous exporter setup has already emitted the same warning.
|
|
85
|
+
"""
|
|
80
86
|
|
|
81
87
|
@dataclass
|
|
82
88
|
class PydanticPlugin:
|
|
@@ -134,9 +140,7 @@ class LocalVariablesOptions:
|
|
|
134
140
|
instrument: bool = ...
|
|
135
141
|
template_mismatch_policy: TemplateMismatchPolicy = ...
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | list[str] | None = None, api_key: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: str | None = None, resource_attributes: Mapping[str, Any] | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | str | None = None, data_dir: Path | str | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, min_level: int | LevelName | None = None, add_baggage_to_attributes: bool = True, code_source: CodeSource | None = None, variables: VariablesOptions | LocalVariablesOptions | None = None, distributed_tracing: bool | None = None, advanced: AdvancedOptions | None = None, **deprecated_kwargs: Unpack[DeprecatedKwargs]) -> Logfire:
|
|
143
|
+
def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | list[str] | None = None, api_key: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: str | None = None, resource_attributes: Mapping[str, Any] | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | str | None = None, data_dir: Path | str | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, min_level: int | LevelName | None = None, add_baggage_to_attributes: bool = True, code_source: CodeSource | None = None, variables: VariablesOptions | LocalVariablesOptions | None = None, distributed_tracing: bool | None = None, advanced: AdvancedOptions | None = None) -> Logfire:
|
|
140
144
|
"""Configure the logfire SDK.
|
|
141
145
|
|
|
142
146
|
Args:
|
|
@@ -192,7 +196,7 @@ def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-
|
|
|
192
196
|
or provide a `MetricsOptions` object to configure metrics, e.g. additional metric readers.
|
|
193
197
|
scrubbing: Options for scrubbing sensitive data. Set to `False` to disable.
|
|
194
198
|
inspect_arguments: Whether to enable
|
|
195
|
-
[f-string magic](https://
|
|
199
|
+
[f-string magic](https://pydantic.dev/docs/logfire/instrument/python/add-manual-tracing/#f-strings).
|
|
196
200
|
If `None` uses the `LOGFIRE_INSPECT_ARGUMENTS` environment variable.
|
|
197
201
|
|
|
198
202
|
Defaults to `True` if and only if the Python version is at least 3.11.
|
|
@@ -264,13 +268,13 @@ class LogfireConfig(_LogfireConfigData):
|
|
|
264
268
|
def initialize(self) -> None:
|
|
265
269
|
"""Configure internals to start exporting traces and metrics."""
|
|
266
270
|
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
267
|
-
"""Force flush all
|
|
271
|
+
"""Force flush all telemetry and forwarding pipelines.
|
|
268
272
|
|
|
269
273
|
Args:
|
|
270
274
|
timeout_millis: The timeout in milliseconds.
|
|
271
275
|
|
|
272
276
|
Returns:
|
|
273
|
-
Whether
|
|
277
|
+
Whether every component that reports a status flushed successfully.
|
|
274
278
|
"""
|
|
275
279
|
def shutdown(self, timeout_millis: int = 30000, flush: bool = True) -> bool:
|
|
276
280
|
"""Shut down variables, forwarding, traces, logs, and metrics."""
|
|
@@ -416,8 +420,15 @@ class LogfireCredentials:
|
|
|
416
420
|
def print_token_summary(self) -> None:
|
|
417
421
|
"""Print a summary of the existing project."""
|
|
418
422
|
|
|
419
|
-
def get_base_url_from_token(token: str) -> str:
|
|
420
|
-
"""Get the base API URL from the token's region.
|
|
423
|
+
def get_base_url_from_token(token: str, warn_unknown_region: bool = True) -> str:
|
|
424
|
+
"""Get the base API URL from the token's region.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
token: The Logfire token to read the region from.
|
|
428
|
+
warn_unknown_region: Whether to emit a `LogfireConfigWarning` when the token's region is
|
|
429
|
+
unrecognised and the US region is used as a fallback. This is on by default for every
|
|
430
|
+
caller; disable it only when the same token has already produced the warning.
|
|
431
|
+
"""
|
|
421
432
|
def get_git_revision_hash() -> str:
|
|
422
433
|
"""Get the current git commit hash."""
|
|
423
434
|
def sanitize_project_name(name: str) -> str:
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import atexit
|
|
2
2
|
import requests
|
|
3
|
-
from ..
|
|
3
|
+
from ..constants import ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY, ATTRIBUTES_SPAN_TYPE_KEY as ATTRIBUTES_SPAN_TYPE_KEY, HTTP_CONNECT_TIMEOUT as HTTP_CONNECT_TIMEOUT, OTLP_MAX_INT_SIZE as OTLP_MAX_INT_SIZE, log_level_attributes as log_level_attributes
|
|
4
|
+
from ..stack_info import STACK_INFO_KEYS as STACK_INFO_KEYS
|
|
5
|
+
from ..utils import logger as logger, platform_is_emscripten as platform_is_emscripten, truncate_string as truncate_string
|
|
4
6
|
from .wrapper import WrapperLogExporter as WrapperLogExporter, WrapperSpanExporter as WrapperSpanExporter
|
|
5
7
|
from _typeshed import Incomplete
|
|
6
8
|
from collections import deque
|
|
7
9
|
from collections.abc import Mapping, Sequence
|
|
8
10
|
from functools import cached_property
|
|
11
|
+
from logfire._internal.utils import handle_internal_errors as handle_internal_errors
|
|
9
12
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
10
13
|
from opentelemetry.sdk._logs import ReadableLogRecord as ReadableLogRecord
|
|
11
|
-
from opentelemetry.sdk.trace import ReadableSpan
|
|
14
|
+
from opentelemetry.sdk.trace import ReadableSpan
|
|
12
15
|
from opentelemetry.sdk.trace.export import SpanExportResult
|
|
13
16
|
from pathlib import Path
|
|
14
17
|
from requests import Session
|
|
@@ -25,6 +28,7 @@ class BodySizeCheckingOTLPSpanExporter(OTLPSpanExporter):
|
|
|
25
28
|
|
|
26
29
|
class OTLPExporterHttpSession(Session):
|
|
27
30
|
"""A requests.Session subclass that defers failed requests to a DiskRetryer."""
|
|
31
|
+
def request(self, method: str, url: str, **kwargs: Any): ...
|
|
28
32
|
def post(self, url: str, data: bytes, **kwargs: Any): ...
|
|
29
33
|
@cached_property
|
|
30
34
|
def retryer(self) -> DiskRetryer: ...
|
|
@@ -59,7 +63,7 @@ class RetryFewerSpansSpanExporter(WrapperSpanExporter):
|
|
|
59
63
|
class BodyTooLargeError(Exception):
|
|
60
64
|
size: Incomplete
|
|
61
65
|
max_size: Incomplete
|
|
62
|
-
def __init__(self, size: int, max_size: int) -> None: ...
|
|
66
|
+
def __init__(self, size: int, max_size: int | None) -> None: ...
|
|
63
67
|
|
|
64
68
|
class SuppressedConnectionError(Exception): ...
|
|
65
69
|
|
|
@@ -4,14 +4,16 @@ from collections.abc import Callable as Callable, Mapping, Sequence
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from logfire._internal.exporters.otlp import OTLPExporterHttpSession as OTLPExporterHttpSession
|
|
7
|
+
from logfire._internal.main import Logfire as Logfire
|
|
7
8
|
from logfire._internal.server_response import install_logfire_response_hook as install_logfire_response_hook
|
|
8
9
|
from logfire._internal.utils import suppress_instrumentation as suppress_instrumentation
|
|
9
|
-
from logfire.experimental.forwarding import ForwardExportRequestResponse as ForwardExportRequestResponse
|
|
10
10
|
from logfire.types import ServerResponseCallback as ServerResponseCallback
|
|
11
11
|
from logfire.version import VERSION as VERSION
|
|
12
12
|
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import ExportLogsServiceResponse
|
|
13
13
|
from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ExportMetricsServiceResponse
|
|
14
14
|
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ExportTraceServiceResponse
|
|
15
|
+
from starlette.requests import Request
|
|
16
|
+
from starlette.responses import Response
|
|
15
17
|
from threading import Thread
|
|
16
18
|
from typing import Literal
|
|
17
19
|
|
|
@@ -48,6 +50,13 @@ class ForwardingErrorResponse:
|
|
|
48
50
|
status_code: int
|
|
49
51
|
content: bytes
|
|
50
52
|
|
|
53
|
+
@dataclass
|
|
54
|
+
class ForwardExportRequestResponse:
|
|
55
|
+
"""Response returned from a forwarded export request to Logfire."""
|
|
56
|
+
status_code: int
|
|
57
|
+
headers: Mapping[str, str]
|
|
58
|
+
content: bytes
|
|
59
|
+
|
|
51
60
|
@dataclass(frozen=True)
|
|
52
61
|
class ForwardingAdmissionResult:
|
|
53
62
|
response: Literal['success', 'partial_success']
|
|
@@ -81,5 +90,15 @@ class OTLPForwardingManager:
|
|
|
81
90
|
|
|
82
91
|
def parse_forwarding_content_type(content_type: str) -> ForwardingContentType | None: ...
|
|
83
92
|
def build_forwarding_request(*, path: str, headers: Mapping[str, str], body: bytes, max_body_size: int = ...) -> ForwardingRequest | ForwardingErrorResponse: ...
|
|
93
|
+
def forward_export_request(*, path: str, headers: Mapping[str, str], body: bytes, logfire_instance: Logfire | None = None, max_body_size: int = ...) -> ForwardExportRequestResponse:
|
|
94
|
+
"""Forward an export request to the Logfire API.
|
|
95
|
+
|
|
96
|
+
See the Logfire.forward_export_request method for more details.
|
|
97
|
+
"""
|
|
98
|
+
async def logfire_proxy(request: Request, *, logfire_instance: Logfire | None = None, max_body_size: int = ...) -> Response:
|
|
99
|
+
"""A Starlette/FastAPI handler to proxy requests to Logfire.
|
|
100
|
+
|
|
101
|
+
See the Logfire.forward_export_request_starlette method for more details.
|
|
102
|
+
"""
|
|
84
103
|
def build_success_response(request: ForwardingRequest) -> ForwardExportRequestResponse: ...
|
|
85
104
|
def build_partial_success_response(request: ForwardingRequest, *, message: str) -> ForwardExportRequestResponse: ...
|
{logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/claude_agent_sdk.pyi
RENAMED
|
@@ -4,6 +4,7 @@ from claude_agent_sdk.types import HookContext, SyncHookJSONOutput
|
|
|
4
4
|
from contextlib import AbstractContextManager
|
|
5
5
|
from logfire._internal.integrations.llm_providers.semconv import CONVERSATION_ID as CONVERSATION_ID, ChatMessage as ChatMessage, ERROR_TYPE as ERROR_TYPE, INPUT_MESSAGES as INPUT_MESSAGES, MessagePart as MessagePart, OPERATION_NAME as OPERATION_NAME, OUTPUT_MESSAGES as OUTPUT_MESSAGES, OutputMessage as OutputMessage, PROVIDER_NAME as PROVIDER_NAME, REQUEST_MODEL as REQUEST_MODEL, RESPONSE_MODEL as RESPONSE_MODEL, ReasoningPart as ReasoningPart, SYSTEM as SYSTEM, SYSTEM_INSTRUCTIONS as SYSTEM_INSTRUCTIONS, TOOL_CALL_ARGUMENTS as TOOL_CALL_ARGUMENTS, TOOL_CALL_ID as TOOL_CALL_ID, TOOL_CALL_RESULT as TOOL_CALL_RESULT, TOOL_NAME as TOOL_NAME, TextPart as TextPart, ToolCallPart as ToolCallPart, ToolCallResponsePart as ToolCallResponsePart
|
|
6
6
|
from logfire._internal.main import Logfire as Logfire, LogfireSpan as LogfireSpan
|
|
7
|
+
from logfire._internal.stack_info import get_user_stack_info as get_user_stack_info
|
|
7
8
|
from logfire._internal.utils import handle_internal_errors as handle_internal_errors
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
@@ -35,7 +36,19 @@ class _ConversationState:
|
|
|
35
36
|
root_span: Incomplete
|
|
36
37
|
active_tool_spans: dict[str, LogfireSpan]
|
|
37
38
|
model: str | None
|
|
39
|
+
announced_tool_ids: set[str]
|
|
40
|
+
tool_announcement_timeout: float
|
|
41
|
+
code_attrs: Incomplete
|
|
38
42
|
def __init__(self, *, logfire: Logfire, root_span: LogfireSpan, input_messages: list[ChatMessage], system_instructions: list[TextPart] | None = None) -> None: ...
|
|
43
|
+
async def wait_for_tool_announcement(self, tool_use_id: str) -> None:
|
|
44
|
+
"""Wait until the assistant message announcing `tool_use_id` has been handled.
|
|
45
|
+
|
|
46
|
+
The CLI sends the assistant message (containing the `tool_use` block) before it
|
|
47
|
+
invokes the PreToolUse hook, but hooks run in separate anyio tasks, so the hook
|
|
48
|
+
can be scheduled before the main task has applied that message to this state.
|
|
49
|
+
Closing the chat span at that point would lose its output and model attributes.
|
|
50
|
+
The timeout is a safety valve for tool calls that are never announced.
|
|
51
|
+
"""
|
|
39
52
|
def add_tool_result(self, tool_use_id: str, tool_name: str, result: Any) -> None:
|
|
40
53
|
"""Record a tool result to include in the next chat span's input messages."""
|
|
41
54
|
def open_chat_span(self) -> None:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from flask.app import Flask
|
|
2
|
-
from logfire._internal.stack_info import warn_at_user_stacklevel as warn_at_user_stacklevel
|
|
3
2
|
from logfire._internal.utils import maybe_capture_server_headers as maybe_capture_server_headers
|
|
4
3
|
from logfire.integrations.flask import CommenterOptions as CommenterOptions, RequestHook as RequestHook, ResponseHook as ResponseHook
|
|
5
4
|
from typing import Any
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import logfire
|
|
2
|
+
from _typeshed import Incomplete
|
|
3
|
+
from logfire._internal.utils import safe_repr as safe_repr
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
CAPTURE_MESSAGE_CONTENT_ENV_VAR: str
|
|
7
|
+
LEGACY_CAPTURE_MESSAGE_CONTENT_VALUES: Incomplete
|
|
8
|
+
EMIT_EVENT_ENV_VAR: str
|
|
9
|
+
original_flatten_compound_value: Incomplete
|
|
10
|
+
|
|
11
|
+
def wrapped_flatten_compound_value(key: str, value: Any, *args: Any, **kwargs: Any): ...
|
|
12
|
+
def instrument_google_genai(logfire_instance: logfire.Logfire, **kwargs: Any): ...
|
{logfire_api-4.40.0 → logfire_api-5.0.0}/logfire_api/_internal/integrations/openai_agents.pyi
RENAMED
|
@@ -7,7 +7,7 @@ from agents.tracing.spans import SpanError, TSpanData
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from logfire import Logfire as Logfire, LogfireSpan as LogfireSpan
|
|
9
9
|
from logfire._internal.formatter import logfire_format as logfire_format
|
|
10
|
-
from logfire._internal.integrations.llm_providers.openai import inputs_to_events as inputs_to_events, responses_output_events as responses_output_events
|
|
10
|
+
from logfire._internal.integrations.llm_providers.openai import get_openai_usage_attributes as get_openai_usage_attributes, inputs_to_events as inputs_to_events, responses_output_events as responses_output_events
|
|
11
11
|
from logfire._internal.scrubbing import NOOP_SCRUBBER as NOOP_SCRUBBER
|
|
12
12
|
from logfire._internal.utils import handle_internal_errors as handle_internal_errors, log_internal_error as log_internal_error, truncate_string as truncate_string
|
|
13
13
|
from openai.types.responses import Response
|
|
@@ -29,6 +29,7 @@ class LogfireTraceProviderWrapper:
|
|
|
29
29
|
class LogfireSpanHelper:
|
|
30
30
|
span: LogfireSpan
|
|
31
31
|
parent: Trace | Span[Any] | None = ...
|
|
32
|
+
openai_cost_provider_url: str | None = ...
|
|
32
33
|
def start(self, mark_as_current: bool): ...
|
|
33
34
|
def end(self, reset_current: bool): ...
|
|
34
35
|
def maybe_detach(self, reset_current: bool): ...
|
|
@@ -94,7 +95,7 @@ class LogfireSpanWrapper(LogfireWrapperBase[Span[TSpanData]], Span[TSpanData]):
|
|
|
94
95
|
@property
|
|
95
96
|
def tracing_api_key(self) -> str | None: ...
|
|
96
97
|
|
|
97
|
-
def attributes_from_span_data(span_data: SpanData, msg_template: str) -> dict[str, Any]: ...
|
|
98
|
+
def attributes_from_span_data(span_data: SpanData, msg_template: str, openai_cost_provider_url: str | None = None) -> dict[str, Any]: ...
|
|
98
99
|
def get_basic_response_attributes(response: Response): ...
|
|
99
|
-
def get_magic_response_attributes() -> dict[str, Any]: ...
|
|
100
|
+
def get_magic_response_attributes() -> tuple[dict[str, Any], str | None]: ...
|
|
100
101
|
def get_response_span_events(span: ResponseSpanData): ...
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
from logfire import Logfire as Logfire
|
|
4
|
+
from typing import Literal
|
|
5
|
+
from typing_extensions import LiteralString
|
|
6
|
+
|
|
7
|
+
MetricName: type[Literal['system.cpu.simple_utilization', 'system.cpu.time', 'system.cpu.utilization', 'system.cpu.load_average.1m', 'system.memory.usage', 'system.memory.utilization', 'system.swap.usage', 'system.swap.utilization', 'system.disk.io', 'system.disk.operations', 'system.disk.time', 'system.network.dropped.packets', 'system.network.packets', 'system.network.errors', 'system.network.io', 'system.network.connections', 'system.process.count', 'system.thread_count', 'process.open_file_descriptor.count', 'process.context_switches', 'process.cpu.time', 'process.cpu.utilization', 'process.cpu.core_utilization', 'process.disk.io', 'process.memory.usage', 'process.memory.virtual', 'process.thread.count', 'process.runtime.gc_count', 'cpython.gc.collected_objects', 'cpython.gc.collections', 'cpython.gc.uncollectable_objects']]
|
|
8
|
+
Config = dict[MetricName, Iterable[str] | None]
|
|
9
|
+
CPU_FIELDS: list[LiteralString]
|
|
10
|
+
MEMORY_FIELDS: list[LiteralString]
|
|
11
|
+
FULL_CONFIG: Config
|
|
12
|
+
BASIC_CONFIG: Config
|
|
13
|
+
Base: Incomplete
|
|
14
|
+
|
|
15
|
+
def get_base_config(base: Base) -> Config: ...
|
|
16
|
+
def instrument_system_metrics(logfire_instance: Logfire, config: Config | None = None, base: Base = 'basic'): ...
|
|
17
|
+
def measure_simple_cpu_utilization(logfire_instance: Logfire): ...
|
|
18
|
+
def measure_process_runtime_cpu_utilization(logfire_instance: Logfire): ...
|
|
19
|
+
def measure_process_cpu_utilization(logfire_instance: Logfire): ...
|
|
20
|
+
def measure_process_cpu_core_utilization(logfire_instance: Logfire):
|
|
21
|
+
"""Same as process.cpu.utilization, but not divided by the number of available cores."""
|
|
22
|
+
def measure_system_cpu_load_average_1m(logfire_instance: Logfire):
|
|
23
|
+
"""1-minute system load average.
|
|
24
|
+
|
|
25
|
+
OTel semconv defines no load average metric. The system metrics spec only
|
|
26
|
+
floats `system.linux.cpu.load_1m` as a hypothetical illustration of OS-prefixed
|
|
27
|
+
naming, so there's nothing to conform to. The name, gauge instrument, and
|
|
28
|
+
`{thread}` unit here match the OTel Collector's `hostmetrics` load scraper,
|
|
29
|
+
which is where most host load averages reaching Logfire come from. Matching it
|
|
30
|
+
means SDK-reported and Collector-reported hosts land on one series instead of two.
|
|
31
|
+
|
|
32
|
+
Upstream `SystemMetricsInstrumentor` doesn't emit this. On Windows,
|
|
33
|
+
`psutil.getloadavg()` is an emulated polling shim — the first call returns 0
|
|
34
|
+
and subsequent values update every ~5s.
|
|
35
|
+
"""
|
|
36
|
+
def measure_system_process_count(logfire_instance: Logfire):
|
|
37
|
+
"""Total number of processes on the system.
|
|
38
|
+
|
|
39
|
+
Name, instrument, and unit follow OTel semconv's `system.process.count`
|
|
40
|
+
(an UpDownCounter in `{process}`). Its `process.state` attribute is omitted:
|
|
41
|
+
`psutil.pids()` is a single cheap syscall, while splitting by state needs a
|
|
42
|
+
`process_iter` over every process and multiplies the series count. This metric
|
|
43
|
+
is in the basic base, so that cost would land on every default install.
|
|
44
|
+
|
|
45
|
+
Note that the OTel Collector's `hostmetrics` receiver still emits the older
|
|
46
|
+
`system.processes.count` for the same measurement.
|
|
47
|
+
|
|
48
|
+
Upstream `SystemMetricsInstrumentor` doesn't emit this.
|
|
49
|
+
"""
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from logfire.exceptions import LogfireConfigError as LogfireConfigError
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
T = TypeVar('T')
|
|
6
|
+
|
|
7
|
+
class NonInteractiveError(LogfireConfigError):
|
|
8
|
+
"""A prompt was needed, but nobody could answer it.
|
|
9
|
+
|
|
10
|
+
Carries the guidance the user needs rather than a traceback: the CLI catches this and
|
|
11
|
+
prints `message` before exiting non-zero. Raised either because `--non-interactive`
|
|
12
|
+
said so up front (`require_answer`), or because stdin ran out before an answer arrived
|
|
13
|
+
(`ask_required`) -- the same question/remedy structure either way (only the reason
|
|
14
|
+
line differs), since both mean the same thing to whoever reads it.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def set_non_interactive(value: bool) -> None:
|
|
18
|
+
"""Record whether prompts are allowed. Called once, from CLI argument parsing."""
|
|
19
|
+
def is_non_interactive() -> bool:
|
|
20
|
+
"""Whether the caller declared that it cannot answer prompts."""
|
|
21
|
+
def require_answer(question: str, remedy: str, *more_remedies: str) -> None:
|
|
22
|
+
"""Fail instead of prompting, when the caller said it cannot answer.
|
|
23
|
+
|
|
24
|
+
At least one remedy is required by the signature: refusing to prompt while offering no
|
|
25
|
+
way forward just moves the dead end, and telling the caller what to pass is the entire
|
|
26
|
+
value of failing here rather than blocking.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
question: what would have been asked, in plain terms.
|
|
30
|
+
remedy: a runnable suggestion.
|
|
31
|
+
more_remedies: further suggestions. Each is printed on its own line, so each must
|
|
32
|
+
be individually pasteable -- `--org a|b` is a shell pipeline, not a suggestion.
|
|
33
|
+
"""
|
|
34
|
+
def ask_or_default(ask: Callable[[], T], default: T) -> T:
|
|
35
|
+
'''Run a prompt, falling back to `default` when stdin runs out before an answer does.
|
|
36
|
+
|
|
37
|
+
Mirrors accepting a blank Enter keypress: the caller already declared what "no answer"
|
|
38
|
+
means by passing this SAME value as the prompt\'s own `default=`, so an exhausted stdin
|
|
39
|
+
reaches the outcome a person pressing Enter would, rather than an `EOFError` traceback.
|
|
40
|
+
Only for prompts where that default is genuinely safe to assume -- see `ask_required`
|
|
41
|
+
for one that has no safe default to fall back on.
|
|
42
|
+
'''
|
|
43
|
+
def ask_required(ask: Callable[[], T], question: str, remedy: str, *more_remedies: str) -> T:
|
|
44
|
+
"""Run a prompt that has no safe default, raising if stdin runs out before an answer does.
|
|
45
|
+
|
|
46
|
+
The reactive half of `require_answer`'s proactive check: that covers a caller who
|
|
47
|
+
declared `--non-interactive` up front, this covers one that did not but ran out of
|
|
48
|
+
input anyway -- a command copied out of the docs into an agent, mid-conversation.
|
|
49
|
+
Raises the same `NonInteractiveError` structure `require_answer` would -- question,
|
|
50
|
+
then reason, then remedies -- with a reason line naming stdin instead of the flag,
|
|
51
|
+
since both mean the same thing to whoever reads it: there was no answer, and here is
|
|
52
|
+
how to supply one.
|
|
53
|
+
"""
|