Function bodies 210 total
RealityEnvironment.step method · python · L161-L187 (27 LOC)src/agent_sim_bridge/environment/real_env.py
def step(self, action: NDArray[np.float32]) -> StepResult:
self._ensure_open()
self._ensure_reset()
t_start = time.monotonic()
obs, reward, terminated, truncated, info = self._interface.system_step(action)
elapsed = time.monotonic() - t_start
if self._step_timeout is not None and elapsed > self._step_timeout:
raise StepTimeoutError(
f"Hardware step took {elapsed:.3f}s, exceeding timeout "
f"of {self._step_timeout}s for environment {self._info.name!r}."
)
self._episode_step += 1
if self._episode_step >= self._info.max_episode_steps:
truncated = True
logger.debug(
"%s step %d: reward=%.4f terminated=%s elapsed=%.3fs",
self._info.name,
self._episode_step,
reward,
terminated,
elapsed,
)
return StepResult(obs, reward, terminated, truncated, info)RealityEnvironment._ensure_reset method · python · L214-L219 (6 LOC)src/agent_sim_bridge/environment/real_env.py
def _ensure_reset(self) -> None:
if self._require_reset and not self._has_reset:
raise RuntimeError(
f"Environment {self._info.name!r} must be reset before "
"the first action is sent to prevent accidental actuation."
)SimBackend.backend_reset method · python · L33-L39 (7 LOC)src/agent_sim_bridge/environment/sim_env.py
def backend_reset(
self,
seed: int | None,
options: dict[str, object] | None,
) -> NDArray[np.float32]:
"""Reset the backend and return the initial observation."""
...SimBackend.backend_step method · python · L41-L46 (6 LOC)src/agent_sim_bridge/environment/sim_env.py
def backend_step(
self,
action: NDArray[np.float32],
) -> tuple[NDArray[np.float32], float, bool, bool, dict[str, object]]:
"""Advance by one step and return ``(obs, reward, terminated, truncated, info)``."""
...SimulationEnvironment.__init__ method · python · L95-L120 (26 LOC)src/agent_sim_bridge/environment/sim_env.py
def __init__(
self,
backend: SimBackend,
name: str = "sim-environment",
max_episode_steps: int = 1000,
record_trajectories: bool = False,
) -> None:
if not isinstance(backend, SimBackend):
raise TypeError(
f"backend must satisfy the SimBackend protocol, got {type(backend)!r}"
)
self._backend = backend
self._info = EnvironmentInfo(
name=name,
is_simulation=True,
max_episode_steps=max_episode_steps,
)
self._record = record_trajectories
self._trajectory: list[
tuple[NDArray[np.float32], NDArray[np.float32], float]
] = []
self._episode_step: int = 0
self._current_obs: NDArray[np.float32] = np.zeros(
self._backend.backend_state_space.shape, dtype=np.float32
)
self._closed: bool = FalseSimulationEnvironment.reset method · python · L154-L166 (13 LOC)src/agent_sim_bridge/environment/sim_env.py
def reset(
self,
*,
seed: int | None = None,
options: dict[str, object] | None = None,
) -> tuple[NDArray[np.float32], dict[str, object]]:
self._ensure_open()
self._trajectory.clear()
self._episode_step = 0
obs = self._backend.backend_reset(seed, options)
self._current_obs = obs
logger.debug("%s reset (seed=%s)", self._info.name, seed)
return obs, {}SimulationEnvironment.step method · python · L168-L180 (13 LOC)src/agent_sim_bridge/environment/sim_env.py
def step(self, action: NDArray[np.float32]) -> StepResult:
self._ensure_open()
obs, reward, terminated, truncated, info = self._backend.backend_step(action)
self._episode_step += 1
self._current_obs = obs
if self._record:
self._trajectory.append((self._current_obs.copy(), action.copy(), reward))
if self._episode_step >= self._info.max_episode_steps:
truncated = True
return StepResult(obs, reward, terminated, truncated, info)Repobility · code-quality intelligence platform · https://repobility.com
GapEstimator.estimate_dimension method · python · L149-L181 (33 LOC)src/agent_sim_bridge/gap/estimator.py
def estimate_dimension(self, dim: GapDimension) -> list[DimensionGap]:
"""Compute all configured metrics for a single dimension.
Parameters
----------
dim:
The :class:`GapDimension` to analyse.
Returns
-------
list[DimensionGap]
One :class:`DimensionGap` per configured metric.
"""
results: list[DimensionGap] = []
for metric in self._metrics:
value = self._compute_metric(metric, dim)
interpretation = self._interpret(metric, value)
results.append(
DimensionGap(
dimension_name=dim.name,
metric=metric,
value=value,
interpretation=interpretation,
)
)
logger.debug(
"Gap[%s, %s] = %.6f (%s)",
dim.name,
metric.value,
value,
interpretationGapEstimator.estimate_all method · python · L183-L198 (16 LOC)src/agent_sim_bridge/gap/estimator.py
def estimate_all(
self, dimensions: list[GapDimension]
) -> dict[str, list[DimensionGap]]:
"""Compute all configured metrics for multiple dimensions.
Parameters
----------
dimensions:
Sequence of :class:`GapDimension` objects.
Returns
-------
dict[str, list[DimensionGap]]
Mapping from dimension name to its list of gap results.
"""
return {dim.name: self.estimate_dimension(dim) for dim in dimensions}GapEstimator.overall_gap_score method · python · L200-L230 (31 LOC)src/agent_sim_bridge/gap/estimator.py
def overall_gap_score(
self, dimension_gaps: dict[str, list[DimensionGap]]
) -> float:
"""Compute a single weighted average gap score in [0, 1].
Each :class:`DimensionGap` value is first normalised to [0, 1] using
the ``"high"`` threshold for the respective metric before being
averaged. This makes the final score interpretable regardless of
the differing natural scales of the individual metrics.
Parameters
----------
dimension_gaps:
Output of :meth:`estimate_all`.
Returns
-------
float
Weighted average gap score ∈ [0, 1]. Returns 0.0 if there are
no results.
"""
normalised_values: list[float] = []
for gaps in dimension_gaps.values():
for gap in gaps:
high_threshold = _THRESHOLDS[gap.metric.value][1]
normalised = min(1.0, gap.value / high_threshold) if high_threshold > 0.0 elseGapEstimator._compute_metric method · python · L236-L263 (28 LOC)src/agent_sim_bridge/gap/estimator.py
def _compute_metric(self, metric: GapMetric, dim: GapDimension) -> float:
"""Dispatch to the correct statistical function for *metric*.
Parameters
----------
metric:
The metric to compute.
dim:
Source of sim/real distributions.
Returns
-------
float
The computed distance value.
"""
sim = dim.sim_distribution
real = dim.real_distribution
if metric is GapMetric.KL_DIVERGENCE:
return kl_divergence(sim, real)
if metric is GapMetric.WASSERSTEIN:
return wasserstein_distance_1d(sim, real)
if metric is GapMetric.MMD:
return maximum_mean_discrepancy(sim, real)
if metric is GapMetric.JENSEN_SHANNON:
return jensen_shannon_divergence(sim, real)
# Unreachable for a well-typed GapMetric, but guards future additions.
raise ValueError(f"Unknown GapMetric: {metric!r}") # pragma:GapEstimator._interpret method · python · L265-L285 (21 LOC)src/agent_sim_bridge/gap/estimator.py
def _interpret(self, metric: GapMetric, value: float) -> str:
"""Map a raw distance value to a qualitative severity label.
Parameters
----------
metric:
The metric whose thresholds to apply.
value:
The computed distance.
Returns
-------
str
``"low"``, ``"medium"``, or ``"high"``.
"""
low_upper, medium_upper = _THRESHOLDS[metric.value]
if value <= low_upper:
return "low"
if value <= medium_upper:
return "medium"
return "high"ProductionTelemetryImporter.load_jsonl method · python · L124-L166 (43 LOC)src/agent_sim_bridge/gap/production_import.py
def load_jsonl(
self, content: str, source_name: str = "unknown"
) -> ImportedTelemetry:
"""Parse JSON Lines format telemetry.
Parameters
----------
content:
The raw JSON Lines string (one JSON object per line).
source_name:
Label for this data source.
Returns
-------
ImportedTelemetry
Parsed telemetry container.
"""
records: list[dict[str, object]] = []
errors = 0
for line in content.splitlines():
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
if isinstance(record, dict):
records.append(record)
else:
errors += 1
except json.JSONDecodeError:
errors += 1
metric_names: set[str] = set()
for record in records:
metric_names.ProductionTelemetryImporter.load_csv method · python · L168-L213 (46 LOC)src/agent_sim_bridge/gap/production_import.py
def load_csv(
self, content: str, source_name: str = "unknown"
) -> ImportedTelemetry:
"""Parse CSV format telemetry.
Parameters
----------
content:
The raw CSV string (first row = header).
source_name:
Label for this data source.
Returns
-------
ImportedTelemetry
Parsed telemetry container.
"""
records: list[dict[str, object]] = []
errors = 0
reader = csv.DictReader(io.StringIO(content))
for row in reader:
parsed_row: dict[str, object] = {}
row_ok = True
for key, value in row.items():
if value is None or value == "":
continue
try:
parsed_row[key] = float(value)
except (ValueError, TypeError):
parsed_row[key] = value # Keep as string if not numeric
if row_ok:
rProductionTelemetryImporter.compare method · python · L219-L283 (65 LOC)src/agent_sim_bridge/gap/production_import.py
def compare(
self,
real_telemetry: ImportedTelemetry,
sim_data: dict[str, list[float]],
) -> list[MetricComparison]:
"""Compare real telemetry against simulated data.
For each metric in *sim_data* that also appears in *real_telemetry*,
compute descriptive statistics and return a :class:`MetricComparison`.
Parameters
----------
real_telemetry:
Imported real-world telemetry.
sim_data:
Mapping of metric name → list of simulated values.
Returns
-------
list[MetricComparison]
One comparison per matched metric.
"""
comparisons: list[MetricComparison] = []
# Extract real values per metric
real_values: dict[str, list[float]] = {}
for record in real_telemetry.records:
for metric, value in record.items():
if isinstance(value, (int, float)):
real_values.setdefaRepobility analyzer · published findings · https://repobility.com
ProductionTelemetryImporter.extract_metric method · python · L285-L307 (23 LOC)src/agent_sim_bridge/gap/production_import.py
def extract_metric(
self, telemetry: ImportedTelemetry, metric_name: str
) -> list[float]:
"""Extract all numeric values for a single metric from telemetry.
Parameters
----------
telemetry:
The imported telemetry.
metric_name:
The metric key to extract.
Returns
-------
list[float]
All numeric values found for the metric.
"""
values: list[float] = []
for record in telemetry.records:
v = record.get(metric_name)
if isinstance(v, (int, float)):
values.append(float(v))
return valuesProductionTelemetryImporter._std method · python · L321-L328 (8 LOC)src/agent_sim_bridge/gap/production_import.py
def _std(values: list[float]) -> float:
"""Compute population standard deviation."""
n = len(values)
if n < 2:
return 0.0
mean = sum(values) / n
variance = sum((v - mean) ** 2 for v in values) / n
return variance ** 0.5GapReporter.generate_report method · python · L87-L126 (40 LOC)src/agent_sim_bridge/gap/report.py
def generate_report(
self,
estimator_results: dict[str, list[DimensionGap]],
dimensions: list[GapDimension],
) -> GapReport:
"""Build a :class:`GapReport` from estimator output.
Parameters
----------
estimator_results:
Output of :meth:`~GapEstimator.estimate_all`.
dimensions:
The original :class:`GapDimension` list (used to build the
estimator on-demand for the overall score).
Returns
-------
GapReport
Fully populated report.
"""
estimator = GapEstimator()
overall_score = estimator.overall_gap_score(estimator_results)
summary = self._generate_summary(overall_score)
recommendations = self._generate_recommendations(estimator_results)
report = GapReport(
report_id=str(uuid.uuid4()),
created_at=datetime.now(tz=timezone.utc),
dimensions=estimator_results,
GapReporter.format_text method · python · L128-L170 (43 LOC)src/agent_sim_bridge/gap/report.py
def format_text(self, report: GapReport) -> str:
"""Render a human-readable plain-text report.
Parameters
----------
report:
The :class:`GapReport` to format.
Returns
-------
str
Multi-line text suitable for terminal output.
"""
lines: list[str] = []
lines.append("=" * 60)
lines.append("Sim-to-Real Gap Report")
lines.append("=" * 60)
lines.append(f"Report ID : {report.report_id}")
lines.append(f"Created At : {report.created_at.isoformat()}")
lines.append(f"Overall Score: {report.overall_score:.4f} (0=no gap, 1=maximum gap)")
lines.append("")
lines.append(f"Summary: {report.summary}")
lines.append("")
if report.dimensions:
lines.append("Dimension Results:")
lines.append("-" * 60)
for dimension_name, gaps in sorted(report.dimensions.items()):
lines.appenGapReporter.format_json method · python · L172-L203 (32 LOC)src/agent_sim_bridge/gap/report.py
def format_json(self, report: GapReport) -> str:
"""Render the report as a JSON string.
Parameters
----------
report:
The :class:`GapReport` to format.
Returns
-------
str
Pretty-printed JSON with 2-space indentation.
"""
payload: dict[str, object] = {
"report_id": report.report_id,
"created_at": report.created_at.isoformat(),
"overall_score": report.overall_score,
"summary": report.summary,
"recommendations": list(report.recommendations),
"dimensions": {
dimension_name: [
{
"metric": gap.metric.value,
"value": gap.value,
"interpretation": gap.interpretation,
}
for gap in gaps
]
for dimension_name, gaps in report.dimensions.items()
GapReporter.format_markdown method · python · L205-L251 (47 LOC)src/agent_sim_bridge/gap/report.py
def format_markdown(self, report: GapReport) -> str:
"""Render the report as a Markdown document.
Parameters
----------
report:
The :class:`GapReport` to format.
Returns
-------
str
Markdown-formatted report suitable for GitHub, Confluence, etc.
"""
lines: list[str] = []
lines.append("# Sim-to-Real Gap Report")
lines.append("")
lines.append(f"**Report ID:** `{report.report_id}`")
lines.append(f"**Created:** {report.created_at.isoformat()}")
lines.append(f"**Overall Score:** {report.overall_score:.4f} *(0 = no gap, 1 = maximum gap)*")
lines.append("")
lines.append(f"## Summary")
lines.append("")
lines.append(report.summary)
lines.append("")
if report.dimensions:
lines.append("## Dimension Results")
lines.append("")
for dimension_name, gaps in sorted(report.dimensiGapReporter._generate_summary method · python · L257-L283 (27 LOC)src/agent_sim_bridge/gap/report.py
def _generate_summary(self, overall_score: float) -> str:
"""Produce a one-sentence summary from the overall score.
Parameters
----------
overall_score:
Weighted average gap score in [0, 1].
Returns
-------
str
Human-readable severity summary.
"""
if overall_score < 0.2:
return (
"The sim-to-real gap is low; the simulation closely matches the "
"real-world distributions."
)
if overall_score < 0.5:
return (
"The sim-to-real gap is moderate; some dimensions show meaningful "
"divergence that may affect transfer performance."
)
return (
"The sim-to-real gap is high; significant distribution mismatches "
"detected across multiple dimensions — transfer is likely unreliable."
)GapReporter._generate_recommendations method · python · L285-L374 (90 LOC)src/agent_sim_bridge/gap/report.py
def _generate_recommendations(
self, dimension_gaps: dict[str, list[DimensionGap]]
) -> list[str]:
"""Produce heuristic recommendations based on gap severity.
Rules applied (in priority order):
- Any ``"high"`` interpretation in any dimension → recommend domain randomisation.
- Any ``"medium"`` or ``"high"`` interpretation → recommend recalibration.
- KL divergence is ``"high"`` → recommend re-examining reward shaping.
- Wasserstein is ``"high"`` → recommend adjusting sim physics parameters.
- MMD is ``"high"`` → recommend increasing sample diversity.
- JSD is ``"high"`` → recommend reviewing observation normalisation.
- All gaps are ``"low"`` → positive feedback.
Parameters
----------
dimension_gaps:
Mapping from dimension name to its list of :class:`DimensionGap` values.
Returns
-------
list[str]
Deduplicated list of actionRepobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
_smooth function · python · L42-L57 (16 LOC)src/agent_sim_bridge/gap/statistics.py
def _smooth(values: list[float], epsilon: float = _EPSILON) -> list[float]:
"""Add *epsilon* to every element to avoid log(0) in KL computation.
Parameters
----------
values:
Raw probability mass values (need not sum to 1).
epsilon:
Additive smoothing constant.
Returns
-------
list[float]
Smoothed values (not renormalised).
"""
return [v + epsilon for v in values]_renormalize function · python · L60-L83 (24 LOC)src/agent_sim_bridge/gap/statistics.py
def _renormalize(values: list[float]) -> list[float]:
"""Divide each element by the total sum.
Parameters
----------
values:
Non-negative floats.
Returns
-------
list[float]
Values that sum to 1.0.
Raises
------
ValueError
If the sum is zero (all values are zero).
"""
total = sum(values)
if total == 0.0:
raise ValueError(
"Cannot normalise a distribution whose elements all sum to zero."
)
return [v / total for v in values]_cdf function · python · L86-L105 (20 LOC)src/agent_sim_bridge/gap/statistics.py
def _cdf(distribution: list[float]) -> list[float]:
"""Compute the empirical CDF from a probability mass vector.
Parameters
----------
distribution:
Probability mass vector (should sum to 1.0; treated as relative weights
if it does not).
Returns
-------
list[float]
Cumulative distribution of the same length.
"""
cumulative: list[float] = []
running_total = 0.0
for mass in distribution:
running_total += mass
cumulative.append(running_total)
return cumulative_percentile function · python · L108-L134 (27 LOC)src/agent_sim_bridge/gap/statistics.py
def _percentile(sorted_values: list[float], fraction: float) -> float:
"""Interpolated percentile for a pre-sorted list.
Uses the linear interpolation method (equivalent to numpy's method=7).
Parameters
----------
sorted_values:
Pre-sorted list of floats (ascending).
fraction:
Fraction in [0, 1] (e.g. 0.25 for Q1, 0.75 for Q3).
Returns
-------
float
The interpolated percentile value.
"""
n = len(sorted_values)
if n == 1:
return sorted_values[0]
index = fraction * (n - 1)
lower = int(index)
upper = lower + 1
if upper >= n:
return sorted_values[n - 1]
weight = index - lower
return sorted_values[lower] + weight * (sorted_values[upper] - sorted_values[lower])normalize_distribution function · python · L142-L167 (26 LOC)src/agent_sim_bridge/gap/statistics.py
def normalize_distribution(values: list[float]) -> list[float]:
"""Normalise a list of non-negative floats so they sum to 1.0.
Parameters
----------
values:
Non-negative floats representing counts, densities, or raw weights.
Returns
-------
list[float]
Values normalised to sum to 1.0.
Raises
------
ValueError
If *values* is empty or all elements are zero.
Examples
--------
>>> normalize_distribution([1.0, 2.0, 1.0])
[0.25, 0.5, 0.25]
"""
if not values:
raise ValueError("Cannot normalise an empty distribution.")
return _renormalize(values)kl_divergence function · python · L170-L217 (48 LOC)src/agent_sim_bridge/gap/statistics.py
def kl_divergence(p: list[float], q: list[float]) -> float:
"""Kullback-Leibler divergence KL(P || Q).
KL(P || Q) = Σ p(i) * log(p(i) / q(i))
Zero-handling is performed by adding *epsilon* = 1e-10 to every element
before normalising, preventing log(0) and division-by-zero.
Parameters
----------
p:
Reference distribution (will be normalised internally).
q:
Approximate distribution (will be normalised internally).
Returns
-------
float
KL divergence ≥ 0. Returns 0.0 for identical distributions.
Raises
------
ValueError
If *p* and *q* have different lengths or are empty.
Notes
-----
The result is not symmetric: KL(P || Q) ≠ KL(Q || P) in general.
Examples
--------
>>> kl_divergence([0.5, 0.5], [0.5, 0.5])
0.0
"""
if not p or not q:
raise ValueError("Distributions must be non-empty.")
if len(p) != len(q):
raise ValueError(
f"Diswasserstein_distance_1d function · python · L220-L268 (49 LOC)src/agent_sim_bridge/gap/statistics.py
def wasserstein_distance_1d(p: list[float], q: list[float]) -> float:
"""Wasserstein-1 (Earth Mover's) distance for 1-D distributions.
Computed as the L1 distance between the cumulative distribution functions:
W₁(P, Q) = Σ |CDF_P(i) - CDF_Q(i)| * bin_width
where bin_width = 1 / n (uniform bins).
Parameters
----------
p:
Probability mass vector for distribution P (will be normalised).
q:
Probability mass vector for distribution Q (will be normalised).
Returns
-------
float
Wasserstein-1 distance ≥ 0. Returns 0.0 for identical distributions.
Raises
------
ValueError
If *p* and *q* have different lengths or are empty.
Examples
--------
>>> wasserstein_distance_1d([1.0, 0.0], [0.0, 1.0])
0.5
"""
if not p or not q:
raise ValueError("Distributions must be non-empty.")
if len(p) != len(q):
raise ValueError(
f"Distributions must have the samaximum_mean_discrepancy function · python · L271-L338 (68 LOC)src/agent_sim_bridge/gap/statistics.py
def maximum_mean_discrepancy(
x: list[float],
y: list[float],
bandwidth: float = 1.0,
) -> float:
"""Maximum Mean Discrepancy with an RBF (Gaussian) kernel.
MMD²(X, Y) = E[k(x, x')] - 2 E[k(x, y)] + E[k(y, y')]
where k(a, b) = exp(-||a - b||² / (2 * bandwidth²)).
MMD is returned as the non-negative square root of MMD².
Parameters
----------
x:
Samples from the first distribution.
y:
Samples from the second distribution.
bandwidth:
RBF kernel bandwidth σ (standard deviation of the Gaussian).
Larger values make the kernel smoother / less sensitive to
fine-grained differences.
Returns
-------
float
MMD ≥ 0. Returns approximately 0 when X and Y are drawn from the
same distribution.
Raises
------
ValueError
If either sample is empty or *bandwidth* ≤ 0.
Examples
--------
>>> maximum_mean_discrepancy([0.0, 1.0], [0.0, 1.0]) # same → ~0
All rows above produced by Repobility · https://repobility.com
jensen_shannon_divergence function · python · L341-L396 (56 LOC)src/agent_sim_bridge/gap/statistics.py
def jensen_shannon_divergence(p: list[float], q: list[float]) -> float:
"""Jensen-Shannon divergence between two distributions.
JSD(P, Q) = 0.5 * KL(P || M) + 0.5 * KL(Q || M)
where M = 0.5 * (P + Q).
JSD is symmetric and bounded in [0, log(2)] (using natural log). It
equals 0 iff P = Q.
Parameters
----------
p:
First distribution (will be normalised internally).
q:
Second distribution (will be normalised internally).
Returns
-------
float
JSD ∈ [0, ln(2)] ≈ [0, 0.693]. Returns 0.0 for identical
distributions.
Raises
------
ValueError
If *p* and *q* have different lengths or are empty.
Examples
--------
>>> jensen_shannon_divergence([0.5, 0.5], [0.5, 0.5])
0.0
"""
if not p or not q:
raise ValueError("Distributions must be non-empty.")
if len(p) != len(q):
raise ValueError(
f"Distributions must have the same length; got {lendescriptive_stats function · python · L399-L447 (49 LOC)src/agent_sim_bridge/gap/statistics.py
def descriptive_stats(values: list[float]) -> dict[str, float]:
"""Compute descriptive statistics for a sample.
Parameters
----------
values:
Sample of floats. Must be non-empty.
Returns
-------
dict[str, float]
Dictionary with keys: ``mean``, ``std``, ``min``, ``max``,
``median``, ``q25``, ``q75``.
Raises
------
ValueError
If *values* is empty.
Examples
--------
>>> stats = descriptive_stats([1.0, 2.0, 3.0, 4.0, 5.0])
>>> stats["mean"]
3.0
"""
if not values:
raise ValueError("descriptive_stats requires at least one value.")
n = len(values)
mean = sum(values) / n
variance = sum((v - mean) ** 2 for v in values) / n
std = math.sqrt(variance)
sorted_values = sorted(values)
minimum = sorted_values[0]
maximum = sorted_values[-1]
median = _percentile(sorted_values, 0.5)
q25 = _percentile(sorted_values, 0.25)
q75 = _percentile(sorted_valuesGapReport.summary method · python · L59-L70 (12 LOC)src/agent_sim_bridge/metrics/gap.py
def summary(self) -> dict[str, float | int | object]:
"""Return a flat dict of all gap metrics."""
return {
"observation_mae": self.observation_mae,
"observation_rmse": self.observation_rmse,
"reward_mae": self.reward_mae,
"reward_bias": self.reward_bias,
"trajectory_length_ratio": self.trajectory_length_ratio,
"overall_gap_score": self.overall_gap_score,
"n_sim_steps": self.n_sim_steps,
"n_real_steps": self.n_real_steps,
}GapReport.compute_overall_score method · python · L72-L113 (42 LOC)src/agent_sim_bridge/metrics/gap.py
def compute_overall_score(
self,
obs_weight: float = 0.5,
reward_weight: float = 0.3,
length_weight: float = 0.2,
) -> float:
"""Compute and store a weighted overall gap score.
The score is a convex combination of sub-metric scores. Sub-metric
contributions are normalised so they are on comparable scales:
* Observation: MAE (already normalised per element).
* Reward: MAE.
* Length: |ratio - 1|.
Parameters
----------
obs_weight:
Weight for observation MAE contribution.
reward_weight:
Weight for reward MAE contribution.
length_weight:
Weight for trajectory length ratio deviation.
Returns
-------
float
The overall gap score (stored in :attr:`overall_gap_score`).
"""
total_weight = obs_weight + reward_weight + length_weight
if total_weight == 0.0:
self.SimRealGap.measure_gap method · python · L139-L219 (81 LOC)src/agent_sim_bridge/metrics/gap.py
def measure_gap(
self,
sim_observations: list[list[float]],
real_observations: list[list[float]],
sim_rewards: list[float] | None = None,
real_rewards: list[float] | None = None,
) -> GapReport:
"""Compute the sim-to-real gap from paired trajectories.
Parameters
----------
sim_observations:
Observation vectors from the simulation trajectory.
real_observations:
Observation vectors from the real trajectory. Must have the same
inner dimensionality as ``sim_observations``.
sim_rewards:
Scalar rewards from the simulation (optional).
real_rewards:
Scalar rewards from the real system (optional).
Returns
-------
GapReport
Computed gap metrics with :attr:`~GapReport.overall_gap_score`
already calculated.
"""
n_sim = len(sim_observations)
n_real = len(real_obse_statistics function · python · L49-L64 (16 LOC)src/agent_sim_bridge/metrics/performance.py
def _statistics(values: list[float]) -> dict[str, float]:
"""Compute min, max, mean, std, and count for a list of floats."""
n = len(values)
if n == 0:
return {"count": 0.0, "min": float("nan"), "max": float("nan"),
"mean": float("nan"), "std": float("nan")}
total = sum(values)
mean = total / n
variance = sum((v - mean) ** 2 for v in values) / n
return {
"count": float(n),
"min": min(values),
"max": max(values),
"mean": mean,
"std": math.sqrt(variance),
}PerformanceTracker.record method · python · L90-L122 (33 LOC)src/agent_sim_bridge/metrics/performance.py
def record(
self,
name: str,
value: float,
step: int | None = None,
tags: list[str] | None = None,
) -> None:
"""Append one value for metric ``name``.
Parameters
----------
name:
Metric identifier.
value:
The scalar value to record.
step:
Optional step index.
tags:
Optional labels.
"""
metric_record = MetricRecord(
name=name,
value=value,
step=step,
tags=tags or [],
)
if name not in self._records:
self._records[name] = []
bucket = self._records[name]
bucket.append(metric_record)
if self._max_records is not None and len(bucket) > self._max_records:
bucket.pop(0)
logger.debug("Recorded metric %r = %.6f (step=%s).", name, value, step)PerformanceTracker.record_many method · python · L124-L135 (12 LOC)src/agent_sim_bridge/metrics/performance.py
def record_many(self, values: dict[str, float], step: int | None = None) -> None:
"""Convenience method to record multiple metrics at once.
Parameters
----------
values:
Mapping of metric name to value.
step:
Shared step index applied to all metrics.
"""
for name, value in values.items():
self.record(name, value, step=step)Repobility · code-quality intelligence platform · https://repobility.com
PerformanceTracker.get_values method · python · L137-L150 (14 LOC)src/agent_sim_bridge/metrics/performance.py
def get_values(self, name: str) -> list[float]:
"""Return all recorded values for metric ``name``.
Parameters
----------
name:
Metric name.
Returns
-------
list[float]
Values in recording order. Empty list if metric not yet recorded.
"""
return [r.value for r in self._records.get(name, [])]PerformanceTracker.summary method · python · L156-L167 (12 LOC)src/agent_sim_bridge/metrics/performance.py
def summary(self) -> dict[str, dict[str, float]]:
"""Compute statistics for every recorded metric.
Returns
-------
dict[str, dict[str, float]]
Mapping of metric name to ``{count, min, max, mean, std}``.
"""
return {
name: _statistics(self.get_values(name))
for name in sorted(self._records)
}PerformanceTracker.metric_summary method · python · L169-L182 (14 LOC)src/agent_sim_bridge/metrics/performance.py
def metric_summary(self, name: str) -> dict[str, float]:
"""Compute statistics for one named metric.
Parameters
----------
name:
Metric name.
Returns
-------
dict[str, float]
``{count, min, max, mean, std}``.
"""
return _statistics(self.get_values(name))PerformanceTracker.reset method · python · L188-L199 (12 LOC)src/agent_sim_bridge/metrics/performance.py
def reset(self, name: str | None = None) -> None:
"""Clear recorded data.
Parameters
----------
name:
If given, clear only that metric. Otherwise clear all metrics.
"""
if name is not None:
self._records.pop(name, None)
else:
self._records.clear()PerformanceTracker.__repr__ method · python · L204-L209 (6 LOC)src/agent_sim_bridge/metrics/performance.py
def __repr__(self) -> str:
return (
f"PerformanceTracker("
f"n_metrics={len(self._records)}, "
f"metrics={self.metric_names()})"
)PluginNotFoundError.__init__ method · python · L55-L62 (8 LOC)src/agent_sim_bridge/plugins/registry.py
def __init__(self, name: str, registry_name: str) -> None:
self.plugin_name = name
self.registry_name = registry_name
super().__init__(
f"Plugin {name!r} is not registered in the {registry_name!r} registry. "
f"Available plugins: {name!r} was not found. "
"Check that the package is installed and its entry-points are declared."
)PluginAlreadyRegisteredError.__init__ method · python · L68-L74 (7 LOC)src/agent_sim_bridge/plugins/registry.py
def __init__(self, name: str, registry_name: str) -> None:
self.plugin_name = name
self.registry_name = registry_name
super().__init__(
f"Plugin {name!r} is already registered in the {registry_name!r} registry. "
"Use a unique name or explicitly deregister the existing entry first."
)PluginRegistry.register method · python · L100-L147 (48 LOC)src/agent_sim_bridge/plugins/registry.py
def register(self, name: str) -> Callable[[type[T]], type[T]]:
"""Return a class decorator that registers the decorated class.
Parameters
----------
name:
The unique string key for this plugin.
Returns
-------
Callable[[type[T]], type[T]]
A decorator that registers the class and returns it unchanged,
allowing it to remain usable as a normal class.
Raises
------
PluginAlreadyRegisteredError
If ``name`` is already in use in this registry.
TypeError
If the decorated class does not subclass ``base_class``.
Example
-------
::
@registry.register("my-plugin")
class MyPlugin(BasePlugin):
...
"""
def decorator(cls: type[T]) -> type[T]:
if name in self._plugins:
raise PluginAlreadyRegisteredError(name, self._name)
if not (Repobility analyzer · published findings · https://repobility.com
PluginRegistry.register_class method · python · L149-L182 (34 LOC)src/agent_sim_bridge/plugins/registry.py
def register_class(self, name: str, cls: type[T]) -> None:
"""Register a class directly without using the decorator syntax.
This is useful for programmatic registration, such as inside
``load_entrypoints``.
Parameters
----------
name:
The unique string key for this plugin.
cls:
The class to register. Must subclass ``base_class``.
Raises
------
PluginAlreadyRegisteredError
If ``name`` is already registered.
TypeError
If ``cls`` is not a subclass of ``base_class``.
"""
if name in self._plugins:
raise PluginAlreadyRegisteredError(name, self._name)
if not (isinstance(cls, type) and issubclass(cls, self._base_class)):
raise TypeError(
f"Cannot register {cls!r} under {name!r}: "
f"it must be a subclass of {self._base_class.__name__}."
)
self._plugins[naPluginRegistry.deregister method · python · L184-L200 (17 LOC)src/agent_sim_bridge/plugins/registry.py
def deregister(self, name: str) -> None:
"""Remove a plugin from the registry.
Parameters
----------
name:
The key previously passed to ``register``.
Raises
------
PluginNotFoundError
If ``name`` is not currently registered.
"""
if name not in self._plugins:
raise PluginNotFoundError(name, self._name)
del self._plugins[name]
logger.debug("Deregistered plugin %r from registry %r", name, self._name)PluginRegistry.get method · python · L206-L227 (22 LOC)src/agent_sim_bridge/plugins/registry.py
def get(self, name: str) -> type[T]:
"""Return the class registered under ``name``.
Parameters
----------
name:
The key used when registering the plugin.
Returns
-------
type[T]
The registered class (not an instance).
Raises
------
PluginNotFoundError
If no plugin is registered under ``name``.
"""
try:
return self._plugins[name]
except KeyError:
raise PluginNotFoundError(name, self._name) from None