← all investigations

Can comet-ml/opik's Python SDK be installed and used to record a trace locally, without a Comet account or a running server?

comet-ml/opik · execution

Verdict

SUPPORTED

blockers: none · not downgraded · stopped: sufficient_info · 11 steps · $0.530 · 318s

passes every criterion

Summary

Sandbox testing confirmed opik's Python SDK can be installed via pip and used with @opik.track plus record_traces_locally() to capture a full trace (name, input, output, spans) entirely in-process, with no Comet account, API key, config file, or reachable server required. Background delivery to comet.com failed due to lack of network, but this did not affect the local recording capability.

Trajectory

step 1 get_repo_metadata({'repo': 'comet-ml/opik'})→ {"full_name": "comet-ml/opik", "license": "Apache-2.0", "language": "Python", "size_kb": 782969, "archived": false, "created_at": "2023-05-10T12:57:13Z", "pushed_at": "2026-09-14T01:13:08Z", "homepage": "https://www.comet.com/docs/opik/", "stars": 22001, "forks": 1787, "open_issues": 252}
step 1 get_repo_description({'repo': 'comet-ml/opik'})→ {"full_name": "comet-ml/opik", "description": "Debug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.", "topics": ["evaluation", "hacktoberfest", "hacktoberfest2025", "langchain", "llama-index", "llm", "llm-evaluation", "llm-observability", "llmops", "open-source", "openai", "playground", "prompt-engineering"]}
step 2 list_files({'repo': 'comet-ml/opik', 'path': 'sdks/python'})→ {"path": "sdks/python", "entries": [{"name": ".ruff.toml", "type": "file", "size": 1261}, {"name": "AGENTS.md", "type": "file", "size": 6364}, {"name": "LICENSE", "type": "file", "size": 11372}, {"name": "README.md", "type": "file", "size": 9217}, {"name": "design", "type": "dir", "size": 0}, {"name": "examples", "type": "dir", "size": 0}, {"name": "pyproject.toml", "type": "file", "size": 202}, {"name": "setup.py", "type": "file", "size": 5470}, {"name": "src", "type": "dir", "size": 0}, {"name": "tests", "type": "dir", "size": 0}], "truncated": false}
step 2 read_file({'repo': 'comet-ml/opik', 'path': 'README.md'})→ {"path": "README.md", "size": 40075, "truncated": true, "content": "<div align=\"center\"><b><a href=\"README.md\">English</a> | <a href=\"readme_CN.md\">\u7b80\u4f53\u4e2d\u6587</a> | <a href=\"readme_ES.md\">Espa\u00f1ol</a> | <a href=\"readme_FR.md\">Fran\u00e7ais</a> | <a href=\"readme_DE.md\">Deutsch</a> | <a href=\"readme_JA.md\">\u65e5\u672c\u8a9e</a></b></div>\n\n\n<h1 align=\"center\" style=\"border-bottom: none\">\n <div>\n <a href=\"https://www.comet.com/site/products/opik/?from=llm&utm_source=opik&utm_medium=github&utm_content=header_img&utm_campaign=opik\"><picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/comet-ml/opik/refs/heads/main/apps/opik-documentation/documentation/static/img/logo-dark-mode.svg\">\n <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/comet-ml/opik/refs/heads/main/apps/opik-documentation/documentation/static/img/opik-logo.svg\">\...
step 3 bash_code_execution({'command': 'echo'})[web]→ null
step 3 list_files({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik'})→ {"path": "sdks/python/src/opik", "entries": [{"name": "__init__.py", "type": "file", "size": 3915}, {"name": "_logging.py", "type": "file", "size": 3104}, {"name": "analytics", "type": "dir", "size": 0}, {"name": "anonymizer", "type": "dir", "size": 0}, {"name": "api_key", "type": "dir", "size": 0}, {"name": "api_objects", "type": "dir", "size": 0}, {"name": "cli", "type": "dir", "size": 0}, {"name": "config.py", "type": "file", "size": 20901}, {"name": "configurator", "type": "dir", "size": 0}, {"name": "context_storage.py", "type": "file", "size": 11871}, {"name": "datetime_helpers.py", "type": "file", "size": 633}, {"name": "decorator", "type": "dir", "size": 0}, {"name": "dict_utils.py", "type": "file", "size": 2280}, {"name": "environment.py", "type": "file", "size": 4644}, {"name": "environment_details.py", "type": "file", "size": 2977}, {"name": "error_tracking", "type": "dir", "size": 0}, {"name": "evaluation", "type": "dir", "size": 0}, {"name": "exceptions.py", "type": "file"...
step 4 read_file({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/config.py'})→ {"path": "sdks/python/src/opik/config.py", "size": 20901, "truncated": true, "content": "from __future__ import annotations\nimport configparser\nimport logging\nimport os\nimport sys\nimport pathlib\nimport urllib.parse\nfrom typing import Any, Dict, Final, List, Literal, Optional, Tuple, Type, Union\n\nimport pydantic\nimport pydantic_settings\nfrom pydantic_settings import BaseSettings, InitSettingsSource\nfrom pydantic_settings.sources import ConfigFileSourceMixin\n\nfrom . import dict_utils, url_helpers\nfrom .api_key import opik_api_key\n\nPathType = Union[\n pathlib.Path,\n str,\n List[Union[pathlib.Path, str]],\n Tuple[Union[pathlib.Path, str], ...],\n]\n\n_SESSION_CACHE_DICT: Dict[str, Any] = {}\n\nMAX_BATCH_SIZE_MB = 5\n\nOPIK_URL_CLOUD: Final[str] = \"https://www.comet.com/opik/api/\"\nOPIK_URL_LOCAL: Final[str] = \"http://localhost:5173/api/\"\n\nOPIK_PROJECT_DEFAULT_NAME: Final[str] = \"Default Project\"\nOPIK_WORKSPACE_DEFAULT_NAME: Final[str] = \"default\"\n\...
step 4 list_files({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/configurator'})→ {"path": "sdks/python/src/opik/configurator", "entries": [{"name": "__init__.py", "type": "file", "size": 0}, {"name": "configure.py", "type": "file", "size": 31540}, {"name": "consent.py", "type": "file", "size": 6008}, {"name": "interactive_helpers.py", "type": "file", "size": 4421}, {"name": "mcp", "type": "dir", "size": 0}, {"name": "opik_rest_helpers.py", "type": "file", "size": 4561}, {"name": "skills", "type": "dir", "size": 0}], "truncated": false}
step 5 list_files({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/message_processing'})→ {"path": "sdks/python/src/opik/message_processing", "entries": [{"name": "__init__.py", "type": "file", "size": 0}, {"name": "api.py", "type": "file", "size": 0}, {"name": "arguments_utils.py", "type": "file", "size": 755}, {"name": "batching", "type": "dir", "size": 0}, {"name": "data_loss.py", "type": "file", "size": 7214}, {"name": "emulation", "type": "dir", "size": 0}, {"name": "encoder_helpers.py", "type": "file", "size": 3022}, {"name": "flush_reporter.py", "type": "file", "size": 2493}, {"name": "message_queue.py", "type": "file", "size": 5109}, {"name": "messages.py", "type": "file", "size": 15350}, {"name": "payload_truncation.py", "type": "file", "size": 6853}, {"name": "permissions", "type": "dir", "size": 0}, {"name": "preprocessing", "type": "dir", "size": 0}, {"name": "processors", "type": "dir", "size": 0}, {"name": "queue_consumer.py", "type": "file", "size": 3832}, {"name": "replay", "type": "dir", "size": 0}, {"name": "streamer.py", "type": "file", "size": 9900}, {"n...
step 5 read_file({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/__init__.py'})→ {"path": "sdks/python/src/opik/__init__.py", "size": 3915, "truncated": false, "content": "from . import _logging, analytics, environment, error_tracking, package_version\nfrom .api_objects.annotation_queue import (\n TracesAnnotationQueue,\n ThreadsAnnotationQueue,\n)\nfrom .api_objects.attachment import Attachment\nfrom .rest_api.types.environment_public import EnvironmentPublic as Environment\nfrom .api_objects import dashboard\nfrom .api_objects.dashboard import Dashboard\nfrom .api_objects.dataset import Dataset\nfrom .api_objects.dataset.test_suite import TestSuite\nfrom .api_objects.dataset.test_suite.types import TestSuiteResult\nfrom .api_objects.experiment.bulk_item import (\n ExperimentItemBulkRecord,\n ExperimentItemBulkSpan,\n ExperimentItemBulkTrace,\n)\nfrom .api_objects.experiment.experiment_item import (\n ExperimentItemContent,\n ExperimentItemReferences,\n)\nfrom .api_objects.agent_config import Config\nfrom .api_objects.agent_config.context impo...
step 6 read_file({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/api_objects/local_recording.py'})→ {"path": "sdks/python/src/opik/api_objects/local_recording.py", "size": 3556, "truncated": false, "content": "import contextlib\nfrom typing import Iterator, List\nfrom typing import Optional\n\nfrom . import opik_client\nfrom .. import exceptions\nfrom ..message_processing.emulation import local_emulator_message_processor, models\nfrom ..message_processing.processors import message_processors_chain\n\n\nclass _LocalRecordingHandle:\n \"\"\"Lightweight handle that exposes locally recorded data.\n\n Accessors flush the client to ensure all in-flight messages are processed\n before reading the in-memory models.\n \"\"\"\n\n def __init__(\n self,\n client: opik_client.Opik,\n local_processor: local_emulator_message_processor.LocalEmulatorMessageProcessor,\n ) -> None:\n self._client = client\n self._local = local_processor\n\n @property\n def span_trees(self) -> List[models.SpanModel]:\n self._client.flush()\n return...
step 7 list_files({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/message_processing/emulation'})→ {"path": "sdks/python/src/opik/message_processing/emulation", "entries": [{"name": "__init__.py", "type": "file", "size": 0}, {"name": "emulator_message_processor.py", "type": "file", "size": 38699}, {"name": "local_emulator_message_processor.py", "type": "file", "size": 5270}, {"name": "models.py", "type": "file", "size": 7423}], "truncated": false}
step 7 read_file({'repo': 'comet-ml/opik', 'path': 'sdks/python/src/opik/message_processing/emulation/local_emulator_message_processor.py'})→ {"path": "sdks/python/src/opik/message_processing/emulation/local_emulator_message_processor.py", "size": 5270, "truncated": false, "content": "import datetime\nimport logging\nfrom typing import List, Dict, Optional, Any, Union\n\nfrom opik.types import ErrorInfoDict, SpanType, TraceSource\nfrom . import models, emulator_message_processor\nfrom .. import messages\nfrom ...rest_api.types import span_write, trace_write\n\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass LocalEmulatorMessageProcessor(\n emulator_message_processor.EmulatorMessageProcessor\n):\n \"\"\"This class serves as a replacement for the real backend and collects all logged messages\n locally in memory to be used for evaluation.\n \"\"\"\n\n def __init__(self, active: bool, merge_duplicates: bool = True) -> None:\n super().__init__(active=active, merge_duplicates=merge_duplicates)\n\n def process(\n self,\n message: Union[\n messages.BaseMessage, span_write.SpanWrite...
step 8 sandbox_clone({'repo': 'comet-ml/opik'})→ {"command": "git clone --depth 1 https://github.com/comet-ml/opik.git /work/repo", "stdout": "", "stderr": "Cloning into '/work/repo'...\nUpdating files: 26% (3034/11366)\nUpdating files: 27% (3069/11366)\nUpdating files: 28% (3183/11366)\nUpdating files: 29% (3297/11366)\nUpdating files: 30% (3410/11366)\nUpdating files: 31% (3524/11366)\nUpdating files: 32% (3638/11366)\nUpdating files: 33% (3751/11366)\nUpdating files: 34% (3865/11366)\nUpdating files: 35% (3979/11366)\nUpdating files: 36% (4092/11366)\nUpdating files: 37% (4206/11366)\nUpdating files: 38% (4320/11366)\nUpdating files: 39% (4433/11366)\nUpdating files: 40% (4547/11366)\nUpdating files: 41% (4661/11366)\nUpdating files: 42% (4774/11366)\nUpdating files: 43% (4888/11366)\nUpdating files: 44% (5002/11366)\nUpdating files: 45% (5115/11366)\nUpdating files: 46% (5229/11366)\nUpdating files: 47% (5343/11366)\nUpdating files: 48% (5456/11366)\nUpdating files: 49% (5570/11366)\nUpdating files: 50% (...
step 9 sandbox_setup({'command': 'cd /work/repo/sdks/python && pip install -e . -q 2>&1 | tail -30'})→ {"command": "cd /work/repo/sdks/python && pip install -e . -q 2>&1 | tail -30", "stdout": " WARNING: The script tqdm is installed in '/work/.local/bin' which is not on PATH.\n Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\n WARNING: The script dotenv is installed in '/work/.local/bin' which is not on PATH.\n Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\n WARNING: The script pygmentize is installed in '/work/.local/bin' which is not on PATH.\n Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\n WARNING: The script idna is installed in '/work/.local/bin' which is not on PATH.\n Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\n WARNING: The script distro is installed in '/work/.local/bin' which is not on PATH.\n Cons...
step 10 sandbox_run({'command': 'cd /work/repo/sdks/python && rm -f ~/.opik.config && unset OPIK_API_KEY OPIK_URL_OVERRIDE && python3 -c "\nimport opik\n\n@opik.track\ndef my_function(x):\n return x * 2\n\nwith opik.record_traces_locally() as storage:\n result = my_function(21)\n traces = storage.trace_trees\n\nprint(\...)→ {"command": "cd /work/repo/sdks/python && rm -f ~/.opik.config && unset OPIK_API_KEY OPIK_URL_OVERRIDE && python3 -c \"\nimport opik\n\n@opik.track\ndef my_function(x):\n return x * 2\n\nwith opik.record_traces_locally() as storage:\n result = my_function(21)\n traces = storage.trace_trees\n\nprint('result:', result)\nprint('num traces recorded:', len(traces))\nprint('trace name:', traces[0].name)\nprint('trace input:', traces[0].input)\nprint('trace output:', traces[0].output)\nprint('spans in trace:', len(traces[0].spans))\n\"\n", "stdout": "\nresult: 42\nnum traces recorded: 1\ntrace name: my_function\ntrace input: {'x': 21}\ntrace output: {'output': 42}\nspans in trace: 1\n", "stderr": "OPIK: ========================\nThe API key must be specified to log data to https://www.comet.com/opik.\nYou can use `opik configure` CLI command to configure your environment for logging.\nSee the configuration details in the docs: https://www.comet.com/docs/opik/tracing/advanced/sdk_conf...

Ledger

reported for The SDK exports a public record_traces_locally() context manager that swaps in a LocalEmulatorMessageProcessor, collecting trace/span data purely in memory instead of sending it to a backend.from: read_file(sdks/python/src/opik/api_objects/local_recording.py)
reported for The config explicitly notes api_key is 'not required if you are running against open source Opik installation', and the default settings loader falls back to defaults with no account required to instantiate the client.from: read_file(sdks/python/src/opik/config.py)
observed for Running a tracked function with record_traces_locally() (no network, no config file, no API key) produced result: 42, num traces recorded: 1, correct trace name/input/output, and 1 span, all captured in-process.from: sandbox_run(record_traces_locally test)
observed unknown Stderr showed warnings that no API key was set and background delivery of trace/span batches to comet.com failed due to connection error, but local trace data was still fully captured and readable via storage.trace_trees.from: sandbox_run(record_traces_locally test)

Facts

setup
222.0s over 1 command(s), install succeeded
disk
721 MB cloned, 1003 MB total
needs
nothing special
exercised
Install and use opik Python SDK to record a trace locally without a Comet account, API key, or running server
output
result: 42, num traces recorded: 1, trace name 'my_function', input {'x': 21}, output {'output': 42}, 1 span

Grounding critique

ran · grounded · no revision