feat: hard cut-over — rename LogStream→AgentStream, Metric→Evaluator#100
Conversation
…tors Introduces the new canonical domain entity names required by HYBIM-730: - **AgentStream** replaces LogStream (new class in agent_stream.py that subclasses LogStream; returns AgentStream from Project methods) - **AgentStreams** replaces LogStreams service class (agent_streams.py); module-level helpers: get_agent_stream, list_agent_streams, create_agent_stream, enable_evaluators - **Evaluator** replaces Metric base class (evaluator.py) - **LlmEvaluator**, **CodeEvaluator**, **LocalEvaluator**, **SplunkAOEvaluator** replace their Metric-prefixed counterparts - **Evaluators** replaces Metrics service class (evaluators.py); helpers: create_custom_llm_evaluator, get_evaluators, delete_evaluator Backward compatibility: - Old names (LogStream, Metric, LlmMetric, …) are preserved via PEP 562 __getattr__ in __init__.py and emit DeprecationWarning - Project.create_log_stream / list_log_streams / logstreams delegate to the new methods with DeprecationWarning - __future__/agent_stream.py and __future__/evaluator.py shims added The underlying API endpoints (/log_streams, /scorers) are unchanged; the server-side rename is tracked separately. Closes HYBIM-730 Co-authored-by: Cursor <cursoragent@cursor.com>
- Add return type -> object to all __getattr__ module functions - Add AgentStream to TYPE_CHECKING import in project.py so string annotations resolve correctly under mypy - Remove incorrect # type: ignore[override] from Evaluator.metrics property and add explicit return type BuiltInEvaluators Co-authored-by: Cursor <cursoragent@cursor.com>
- evaluator.py: remove deprecated `metrics` property; mypy forbids overriding a writable class attribute (Metric.metrics = BuiltInMetrics()) with a read-only property in a subclass. Evaluator.evaluators remains the canonical accessor. - project.py: add cast() around AgentStream.create() and AgentStream.list() return values. Both methods inherit LogStream's signature (-> LogStream / -> list[LogStream]); cast tells mypy the runtime types are AgentStream. Co-authored-by: Cursor <cursoragent@cursor.com>
fercor-cisco
left a comment
There was a problem hiding this comment.
🤖 This review was generated by the Astra agent (claude-opus-4-8). It may contain mistakes.
Verdict: request_changes — Public convenience functions for agent streams return broken objects; deprecation shims silently never fire.
General Comments
- 🟠 major (testing): The test plan lists a manual import/deprecation smoke test but no automated tests were added, and the checked items don't cover the module-level convenience functions (
get_agent_stream/list_agent_streams/create_agent_stream) or the module-level deprecation aliases — which is exactly where the bugs are. A rename PR advertising "full backward compatibility" should include tests that (1) round-trip an AgentStream through each convenience function and assert.name/.id/is_synced(), (2) assert each deprecated module symbol emitsDeprecationWarning, and (3) assertEvaluator.get()/.list()return the expected type. All three would have caught the issues flagged here.
Follow-ups
Suggested follow-up work that could be tracked as Shortcut stories:
src/splunk_ao/evaluators.py:15-20: Import ordering:from galileo_core.schemas.logging.step import StepType(line 19) is placed after thesplunk_ao.*imports, which violates the isort/ruff grouping used elsewhere in the repo. Runruff/isort to reorder (third-party before first-party).src/splunk_ao/agent_streams.py:164-170:enable_evaluatorsdoesfrom splunk_ao.log_streams import LogStreamsinside the function body even thoughLogStreamsis already imported at module top (line 17). Drop the redundant local import.src/splunk_ao/project.py:1139-1157: The deprecatedcreate_log_stream/list_log_streamsdocstrings were rewritten to say "Create/List a new agent stream", which reads oddly for a method named*_log_stream. Consider keeping the summary line referring to log streams and letting the.. deprecated::note point to the agent-stream replacement, so the deprecation intent stays clear.
| result = get_log_stream(name=name, project_id=project_id, project_name=project_name) | ||
| if result is None: | ||
| return None | ||
| stream = AgentStream.__new__(AgentStream) | ||
| stream.__dict__.update(result.__dict__) | ||
| return stream |
There was a problem hiding this comment.
🔴 critical (bug): This produces a broken AgentStream. get_log_stream() returns the service-layer splunk_ao.log_streams.LogStream (a subclass of LogStreamResponse, decorated with @attrs.define → slots). Its fields (id, name, project_id, ...) live in slots, so result.__dict__ does not contain them — stream.__dict__.update(result.__dict__) copies essentially nothing. Combined with AgentStream.__new__ bypassing __init__ (so _sync_state, project_name, etc. are never set), the returned object raises AttributeError on stream.name, stream.id, str(stream), is_synced(). The two LogStream classes also have incompatible schemas. AgentStream already inherits a working get() from the object-centric LogStream that returns AgentStream instances via cls._from_api_response; delegate to it instead.
| result = get_log_stream(name=name, project_id=project_id, project_name=project_name) | |
| if result is None: | |
| return None | |
| stream = AgentStream.__new__(AgentStream) | |
| stream.__dict__.update(result.__dict__) | |
| return stream | |
| return AgentStream.get(name=name, project_id=project_id, project_name=project_name) |
🤖 Generated by the Astra agent
| log_streams = list_log_streams( | ||
| project_id=project_id, | ||
| project_name=project_name, | ||
| limit=limit, | ||
| starting_token=starting_token, | ||
| ) | ||
| result: builtins.list[AgentStream] = [] | ||
| for ls in log_streams: | ||
| s = AgentStream.__new__(AgentStream) | ||
| s.__dict__.update(ls.__dict__) | ||
| result.append(s) | ||
| return result |
There was a problem hiding this comment.
🔴 critical (bug): Same defect as get_agent_stream: list_log_streams() returns slotted service-layer LogStream objects, so s.__dict__.update(ls.__dict__) copies nothing and AgentStream.__new__ skips initialization. Every returned object is a broken AgentStream that raises AttributeError on attribute access. Delegate to the inherited classmethod, which already returns AgentStream instances.
| log_streams = list_log_streams( | |
| project_id=project_id, | |
| project_name=project_name, | |
| limit=limit, | |
| starting_token=starting_token, | |
| ) | |
| result: builtins.list[AgentStream] = [] | |
| for ls in log_streams: | |
| s = AgentStream.__new__(AgentStream) | |
| s.__dict__.update(ls.__dict__) | |
| result.append(s) | |
| return result | |
| return AgentStream.list( | |
| project_id=project_id, | |
| project_name=project_name, | |
| limit=limit, | |
| starting_token=starting_token, | |
| ) |
🤖 Generated by the Astra agent
| ls = create_log_stream(name=name, project_id=project_id, project_name=project_name) | ||
| stream = AgentStream.__new__(AgentStream) | ||
| stream.__dict__.update(ls.__dict__) | ||
| return stream |
There was a problem hiding this comment.
🔴 critical (bug): Same defect: create_log_stream() returns a slotted service-layer LogStream, so stream.__dict__.update(ls.__dict__) copies no fields and __init__/_sync_state are never set. Use the object-centric create path instead, which returns a fully-initialized AgentStream.
| ls = create_log_stream(name=name, project_id=project_id, project_name=project_name) | |
| stream = AgentStream.__new__(AgentStream) | |
| stream.__dict__.update(ls.__dict__) | |
| return stream | |
| return AgentStream(name=name, project_id=project_id, project_name=project_name).create() |
🤖 Generated by the Astra agent
| from splunk_ao.metric import ( | ||
| BuiltInMetrics, | ||
| CodeMetric, | ||
| LlmMetric, | ||
| LocalMetric, | ||
| Metric, | ||
| SplunkAOMetric, |
There was a problem hiding this comment.
🟠 major (bug): These top-level imports make Metric, LlmMetric, CodeMetric, LocalMetric, SplunkAOMetric, and BuiltInMetrics real module globals, so the deprecation __getattr__ at line 164 is never invoked for them (module __getattr__ only fires when normal attribute lookup fails). Accessing splunk_ao.evaluator.Metric returns the class with no DeprecationWarning, contradicting the module docstring and the PR's stated backward-compat contract. To make the shim work, import these under private names (e.g. from splunk_ao.metric import Metric as _Metric) so the deprecated public names resolve only via __getattr__, and reference the private names in the class bases below.
🤖 Generated by the Astra agent
| import datetime | ||
| import warnings | ||
|
|
||
| from splunk_ao.metrics import Metrics, create_custom_llm_metric, delete_metric, get_metrics |
There was a problem hiding this comment.
🟠 major (bug): create_custom_llm_metric, delete_metric, and get_metrics are imported as module globals here, so the deprecation __getattr__ at line 180 never fires for them — accessing splunk_ao.evaluators.get_metrics returns the function with no warning. Import them under private aliases (e.g. create_custom_llm_metric as _create_custom_llm_metric) so the deprecated names are only reachable through __getattr__.
🤖 Generated by the Astra agent
| import warnings | ||
|
|
||
| from splunk_ao.agent_stream import AgentStream | ||
| from splunk_ao.log_streams import LogStreams, create_log_stream, get_log_stream, list_log_streams |
There was a problem hiding this comment.
🟠 major (bug): create_log_stream, get_log_stream, and list_log_streams are imported as module globals, so the deprecation __getattr__ at line 193 only ever fires for enable_metrics (the one name not imported here). The other three deprecated aliases never emit a warning. Import them under private names so the deprecated symbols resolve only via __getattr__.
🤖 Generated by the Astra agent
There was a problem hiding this comment.
src/splunk_ao/evaluator.py:843-855 (line not in diff)
🟠 major (bug): Evaluator.get()/Evaluator.list() are inherited from Metric and route through Metric._create_metric_from_type, which hardcodes LlmMetric/CodeMetric/SplunkAOMetric — never the *Evaluator subclasses. So Evaluator.get(name="correctness") returns a SplunkAOMetric, and this docstring's assert isinstance(ev, SplunkAOEvaluator) is false. The rename doesn't surface new types on read paths. Either override _create_metric_from_type in Evaluator to return the Evaluator subclasses, or correct the docstrings to state that get()/list() return Metric-family instances.
🤖 Generated by the Astra agent
|
@adityamehra as discussed in the ticket, let's not add a deprecated shim layer, the plan is to do a pure renaming. The migration guide / tool will be used for customers that need to migrate their code. Backward compatibility is provided via the |
Domain entity renames (per PR review: no deprecated wrappers): LogStream → AgentStream - src/splunk_ao/log_stream.py → agent_stream.py (AgentStream class) - src/splunk_ao/log_streams.py → agent_streams.py (AgentStreams service) - enable_metrics() → enable_evaluators() - get_log_stream/list_log_streams/create_log_stream → get/list/create_agent_stream - project.py: remove deprecated create_log_stream/list_log_streams/logstreams Metric → Evaluator - src/splunk_ao/metric.py → evaluator.py (Evaluator, LlmEvaluator, CodeEvaluator, LocalEvaluator, SplunkAOEvaluator, BuiltInEvaluators) - src/splunk_ao/metrics.py → evaluators.py (Evaluators service, renamed convenience functions) - delete_metric → delete_evaluator - create_custom_llm_metric → create_custom_llm_evaluator - get_metrics → get_evaluators Other - __init__.py: remove PEP 562 deprecated __getattr__ shims; export only new names - __future__/__init__.py: updated to new class names - types.py: MetricSpec uses Evaluator - export.py, logger/logger.py: updated to AgentStreams - All test files updated (imports, @patch paths, assertions, param names) - Tests renamed: test_log_stream* → test_agent_stream*, test_metric* → test_evaluator* Results: 1856 passed, 18 failed (all pre-existing), 3 error (pre-existing) Type-check: Success (107 source files) Co-authored-by: Cursor <cursoragent@cursor.com>
fercor-cisco
left a comment
There was a problem hiding this comment.
🤖 This review was generated by the Astra agent (claude-opus-4-8). It may contain mistakes.
Verdict: request_changes — Code cut-over is sound, but the newly-added doc file documents the abandoned backward-compat design and contradicts the implementation.
Follow-ups
Suggested follow-up work that could be tracked as Shortcut stories:
src/splunk_ao/shared/column.py:62-62: Pre-existing docstrings across shared/ (column.py:62,525; query_result.py:65) still useLogStream.get(...)in examples. Not introduced by this PR, but worth a sweep to align all docstring examples with the AgentStream/Evaluator names now that the old names are gone.src/splunk_ao/agent_stream.py:47-99: AgentStream/AgentStreams class and method docstrings throughout still say "log stream"/"metrics" in prose and referencesplunk_ao.log_streamsimports. Functionally harmless, but a follow-up doc pass to rename the user-facing prose to "agent stream"/"evaluators" would complete the rename intent.
| ev = Evaluator.evaluators.correctness | ||
| ev = Evaluator.evaluators.completeness |
There was a problem hiding this comment.
🟡 minor (documentation): Evaluator.evaluators.correctness is wrong — the built-in accessor attribute is named metrics (with a legacy scorers alias); see evaluator.py:145,148. There is no evaluators attribute, so this raises AttributeError. Even after the larger doc rewrite, use Evaluator.metrics.correctness.
| ev = Evaluator.evaluators.correctness | |
| ev = Evaluator.evaluators.completeness | |
| # Access built-in evaluators | |
| ev = Evaluator.metrics.correctness | |
| ev = Evaluator.metrics.completeness |
🤖 Generated by the Astra agent
There was a problem hiding this comment.
src/splunk_ao/agent_stream.py:470-478 (line not in diff)
🟡 minor (documentation): set_metrics docstring example imports and uses Metric (from splunk_ao import Metric, Metric.metrics.correctness, Metric.get(...)), which no longer exists after the cut-over. Update to Evaluator. Line 78 similarly shows project.create_log_stream(...) which was renamed to create_agent_stream.
| from splunk_ao import Evaluator, AgentStream | |
| log_stream = AgentStream.get(name="Production Logs", project_name="My Project") | |
| # Set metrics (replaces existing) | |
| log_stream.set_metrics([ | |
| Evaluator.metrics.correctness, | |
| Evaluator.metrics.completeness, | |
| Evaluator.get(id="metric-from-console-uuid"), # From console | |
| ]) |
🤖 Generated by the Astra agent
There was a problem hiding this comment.
🟠 major (documentation): This doc, added by this PR, describes the abandoned shim/backward-compat design rather than the hard cut-over that was actually implemented. It is broadly and actively misleading:
- Header says "Status: Implemented with full backward compatibility" and the whole "Backward Compatibility" section (lines 144-186) claims
from splunk_ao import LogStream/Metric/LlmMetric/...andproject.create_log_stream()/list_log_streams()/logstreamsstill work withDeprecationWarning. In reality the old modules and__getattr__shims were deleted andproject.pyno longer definescreate_log_stream/list_log_streams/logstreams— every one of these snippets now raisesImportError/AttributeError. - The "Files Changed" table (lines 194-201) lists
src/splunk_ao/__future__/agent_stream.pyand__future__/evaluator.pyas "New — deprecation shim"; those files do not exist (the PR body itself says they were removed). It also describesAgentStreamas a "subclass of LogStream" andEvaluatorvia a PEP 562__getattr__, neither of which matches the code. - The "Design Decisions" section (lines 206-229) documents subclassing-LogStream and
__getattr__-deprecation rationales that no longer apply.
This doc should be rewritten to describe the hard cut-over (old names gone, no shims, migration via the separate migration guide / galileo SDK) or removed in favor of the migration guide referenced in the PR body.
🤖 Generated by the Astra agent
There was a problem hiding this comment.
🟡 minor (documentation): Class/method docstrings here still reference deleted names, so copy-pasting the examples fails after this hard cut-over. Lines 63 from splunk_ao.metric import Evaluator (module deleted), 118 & 1174 from splunk_ao import ... LogStream and 120 & 1176 LogStream.get(...) (LogStream no longer importable — use AgentStream). Update these examples to the new names as the ticket requests ("we also want the examples and code snippets to use the renamed entity names").
🤖 Generated by the Astra agent
- docs/HYBIM-730-domain-entity-rename.md: complete rewrite to describe
the hard cut-over; removes all backward-compat/shim content that
described the abandoned design (old doc claimed LogStream/Metric still
worked with DeprecationWarning, referenced deleted __future__ files,
described AgentStream as a LogStream subclass). Now accurately reflects
the pure rename: old modules gone, no shims, migration via galileo SDK.
Also adds HYBIM-914 to the Related section.
- evaluator.py: fix stale references in class docstrings
* BuiltInEvaluators example: 'from splunk_ao.metric import Evaluator'
→ 'from splunk_ao import Evaluator'
* Evaluator class example: LogStream → AgentStream
* SplunkAOEvaluator class example: LogStream → AgentStream
- agent_stream.py: fix stale references in docstrings
* AgentStream class example: project.create_log_stream → create_agent_stream
* set_metrics example: 'from splunk_ao import Metric' → Evaluator,
Metric.metrics.* → Evaluator.metrics.*
- agent_streams.py: update all docstring examples that still imported
from the deleted splunk_ao.log_streams module
* 'from splunk_ao.log_streams import ...' →
'from splunk_ao.agent_streams import ...' (7 occurrences in docstrings)
Type-check: success (112 source files)
Tests: 1913 passed / 15 failed (pre-existing) / 3 error (pre-existing)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
fercor-cisco
left a comment
There was a problem hiding this comment.
🤖 This review was generated by the Astra agent (claude-sonnet-5). It may contain mistakes.
Verdict: approve — The hard cut-over is implemented consistently and correctly at HEAD — the old shim-based issues raised in earlier review rounds (broken slotted-object copies, imports bypassing getattr deprecation shims, Evaluator.get() returning the wrong subclass) have all been resolved by fully reimplementing the new files rather than wrapping the old ones. Remaining issues are minor/doc-scoped.
General Comments
- 🟡 minor (documentation): This is a breaking, hard cut-over rename (old top-level names
LogStream,Metric,LlmMetric, etc. and modulessplunk_ao.log_stream(s)/splunk_ao.metric(s)are gone entirely), butCHANGELOG.mdhas no entry for it. Given the PR explicitly states 'No backward compatibility shims — old names are gone,' users upgrading in-place will hitImportError/AttributeErrorwith no changelog signal. Add an Unreleased/Breaking Changes entry pointing to the migration guide.
Summary
Hard cut-over rename of SDK domain entities. No backward compatibility shims — old names are gone. Migration is handled by a separate migration guide.
LogStreamAgentStreamLogStreamsAgentStreamsMetricEvaluatorLlmMetricLlmEvaluatorCodeMetricCodeEvaluatorLocalMetricLocalEvaluatorSplunkAOMetricSplunkAOEvaluatorBuiltInMetricsBuiltInEvaluatorsMetrics(service)Evaluatorsget_log_stream()get_agent_stream()list_log_streams()list_agent_streams()create_log_stream()create_agent_stream()enable_metrics()enable_evaluators()delete_metric()delete_evaluator()create_custom_llm_metric()create_custom_llm_evaluator()get_metrics()get_evaluators()The underlying API endpoints (
/log_streams,/scorers) are unchanged — this is a client-side rename only. Server-side rename is tracked separately.New API
Files changed
Source — old files deleted, new files are the full implementation (not wrappers):
src/splunk_ao/log_stream.pysrc/splunk_ao/agent_stream.pysrc/splunk_ao/log_streams.pysrc/splunk_ao/agent_streams.pysrc/splunk_ao/metric.pysrc/splunk_ao/evaluator.pysrc/splunk_ao/metrics.pysrc/splunk_ao/evaluators.pysrc/splunk_ao/__future__/log_stream.pysrc/splunk_ao/__future__/metric.pysrc/splunk_ao/__future__/agent_stream.pysrc/splunk_ao/__future__/evaluator.pyOther source files updated:
src/splunk_ao/__init__.py— removed deprecated__getattr__shims; exports only new namessrc/splunk_ao/project.py— removedcreate_log_stream,list_log_streams,logstreams; keepscreate_agent_stream,list_agent_streams,agent_streamssrc/splunk_ao/__future__/__init__.py— updated to new class namessrc/splunk_ao/export.py,src/splunk_ao/logger/logger.py— updated toAgentStreamssrc/splunk_ao/types.py—MetricSpecnow referencesEvaluatorTests renamed and updated:
tests/test_log_stream.pytests/test_agent_stream.pytests/test_log_streams_metrics.pytests/test_agent_streams_evaluators.pytests/test_log_streams_pagination.pytests/test_agent_streams_pagination.pytests/test_metric.pytests/test_evaluator.pytests/test_metric_types.pytests/test_evaluator_types.pytests/test_metrics.pytests/test_evaluators.pyAll
@patchpaths, import statements, assertions, and parameter names updated across the full test suite.Verification
poetry run invoke type-checkpoetry run invoke testRelated