Files
wehub-resource-sync 5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:25:44 +08:00

504 lines
17 KiB
Python

import argparse
import inspect
import os
import re
from pathlib import Path
from typing import Any, get_type_hints
# Suppress banner for helper scripts
os.environ["OPIK_OPTIMIZER_NO_BANNER"] = "1"
import opik_optimizer # noqa: E402
from pydantic import BaseModel
class ParameterInfo(BaseModel):
path: str
type: str | None
default: Any | None
required: bool
description: str | None = None
class MethodInfo(BaseModel):
name: str
docstring: str | None
parameters: list[ParameterInfo]
class ClassInfo(BaseModel):
init_parameters: list[ParameterInfo]
public_methods: list[MethodInfo]
class ClassInspector:
def __init__(self, cls: type):
self.cls = cls
def get_docstring(self) -> str | None:
return inspect.getdoc(self.cls)
def parse_param_descriptions(self, docstring: str) -> dict[str, str]:
"""
Parses the 'Args:' section of a docstring and extracts parameter descriptions.
Handles multi-line descriptions by continuing until the next parameter or end of Args section.
"""
param_desc: dict[str, str] = {}
if not docstring:
return param_desc
lines = docstring.splitlines()
in_args_section = False
current_param = None
current_description_lines = []
for line in lines:
stripped = line.strip()
# Check if we're entering the Args section
if stripped.startswith("Args:") or stripped.startswith("Arguments:"):
in_args_section = True
continue
if in_args_section:
# Check if this is a new parameter (starts with word followed by colon)
match = re.match(r"^(\**\w+):\s*(.*)", stripped)
if match:
# Save previous parameter if exists
if current_param:
param_desc[current_param] = " ".join(current_description_lines)
# Start new parameter
param_name, description = match.groups()
current_param = param_name
current_description_lines = [description] if description else []
elif stripped == "":
# Empty line might indicate end of Args section
# But only if we haven't started a parameter yet, or if next non-empty line doesn't continue
continue
elif current_param and stripped:
# Continuation of current parameter description
current_description_lines.append(stripped)
elif not stripped and current_param:
# Empty line while processing a parameter - might be end of Args
continue
else:
# Non-parameter line that doesn't match expected format - end of Args section
break
# Don't forget to save the last parameter
if current_param:
param_desc[current_param] = " ".join(current_description_lines)
return param_desc
def get_pydantic_model_fields(self) -> list[ParameterInfo]:
fields = []
for name, model_field in self.cls.model_fields.items(): # type: ignore
default = model_field.default if model_field.default is not None else None
is_required = model_field.is_required()
annotation = model_field.annotation
try:
type_str = str(annotation).replace("typing.", "")
except Exception:
type_str = "Any"
description = model_field.description or None
fields.append(
ParameterInfo(
path=name,
type=type_str,
default=default,
required=is_required,
description=description,
)
)
return fields
def parse_signature(
self,
func: Any,
docstring: str | None = None,
) -> list[ParameterInfo]:
sig = inspect.signature(func)
try:
type_hints = get_type_hints(func, globalns=vars(inspect.getmodule(func)))
except Exception:
type_hints = {}
param_docs = self.parse_param_descriptions(docstring or "")
parameters = []
for param in sig.parameters.values():
if param.name == "self":
continue
param_type = type_hints.get(param.name, None)
if param_type is None:
param_type_str = "Any"
else:
param_type_str = (
param_type.__name__
if hasattr(param_type, "__name__")
else str(param_type)
)
is_required = param.default is inspect._empty
default_value = None if is_required else param.default
parameters.append(
ParameterInfo(
path=param.name,
type=param_type_str,
default=default_value,
required=is_required,
description=param_docs.get(param.name),
)
)
return parameters
def get_public_methods(self) -> list[MethodInfo]:
if issubclass(self.cls, BaseModel):
return []
methods = inspect.getmembers(self.cls, predicate=inspect.isfunction)
public_methods_info = []
for name, func in methods:
if name.startswith("_") and name != "__init__":
continue
if name == "__init__":
continue # Handled separately
# Skip methods that are not defined in this class if they raise NotImplementedError
# (inherited from base and not overridden)
if not self._is_method_defined_in_class(name):
if self._method_raises_not_implemented(func):
continue
# Skip methods that are defined in this class but immediately raise NotImplementedError
if self._is_method_defined_in_class(name):
if self._method_raises_not_implemented(func):
continue
doc = inspect.getdoc(func) or ""
parameters = self.parse_signature(func, doc)
public_methods_info.append(
MethodInfo(name=name, docstring=doc, parameters=parameters)
)
return public_methods_info
def _is_method_defined_in_class(self, method_name: str) -> bool:
"""Check if a method is defined in the class itself, not just inherited."""
return method_name in self.cls.__dict__
def _method_raises_not_implemented(self, func: Any) -> bool:
"""Check if a method only raises NotImplementedError without doing anything else."""
try:
source = inspect.getsource(func)
lines = source.split("\n")
# Find where the function body starts (after the def line and parameters)
body_start = 0
in_signature = True
for i, line in enumerate(lines):
if in_signature:
# Look for the closing of the function signature
if ")" in line and ":" in line:
body_start = i + 1
in_signature = False
break
# Parse the body to find actual code (skip docstrings)
body_lines = lines[body_start:]
in_docstring = False
docstring_char = None
actual_code_lines = []
for line in body_lines:
stripped = line.strip()
# Handle docstring start/end
if not in_docstring:
if stripped.startswith('"""') or stripped.startswith("'''"):
docstring_char = stripped[:3]
in_docstring = True
# Check if docstring ends on same line
if stripped.endswith(docstring_char):
in_docstring = False
continue
else:
# We're in a docstring, check if it ends
if docstring_char is not None and docstring_char in stripped:
in_docstring = False
continue
# Skip empty lines and comments
if not stripped or stripped.startswith("#"):
continue
# This is actual code
actual_code_lines.append(stripped)
# Stop after finding a few lines of actual code
if len(actual_code_lines) >= 5:
break
# If the only actual code is raising NotImplementedError, skip this method
if actual_code_lines:
# Check if first line is raise NotImplementedError
if actual_code_lines[0].startswith("raise NotImplementedError"):
return True
return False
except (OSError, TypeError):
# Can't get source, assume it's implemented
return False
def inspect(self) -> ClassInfo:
if issubclass(self.cls, BaseModel):
init_params = self.get_pydantic_model_fields()
else:
init_func = getattr(self.cls, "__init__", None)
# Use class docstring for parameter descriptions, not __init__ docstring
# This is because Python conventionally puts parameter docs in the class docstring
class_doc = self.get_docstring()
init_params = (
self.parse_signature(init_func, class_doc) if init_func else []
)
return ClassInfo(
init_parameters=init_params,
public_methods=self.get_public_methods(),
)
def format_param_field(self, param: ParameterInfo) -> str:
def _escape_mdx_text(text: str) -> str:
return text.replace("{", "{").replace("}", "}")
field = f'<ParamField path="{param.path}" type="{param.type or "Any"}"'
if not param.required:
field += " optional={true}"
if param.default is not None and param.default != inspect._empty:
field += f' default="{param.default}"'
if param.description:
field += f">{_escape_mdx_text(param.description)}</ParamField>"
else:
field += " />"
return field
def format_method_signature(
self, name: str, parameters: list[ParameterInfo]
) -> str:
if len(parameters) == 0:
lines = [f"{name}()"]
else:
lines = [f"{name}("]
param_lines = []
for param in parameters:
type_str = param.type or "Any"
if not param.required:
default_repr = f" = {repr(param.default)}"
else:
default_repr = ""
param_lines.append(f" {param.path}: {type_str}{default_repr}")
if param_lines:
lines.append(",\n".join(param_lines))
lines.append(")")
lines = ["```python"] + lines + ["```\n"]
return "\n".join(lines)
def to_render_string(self) -> str:
info = self.inspect()
output = []
# Class header and constructor
output.append(f"## {self.cls.__name__}\n")
output.append(
self.format_method_signature(self.cls.__name__, info.init_parameters)
)
output.append("")
if info.init_parameters and len(info.init_parameters) > 0:
output.append("**Parameters:**")
output.append("")
for param in info.init_parameters:
output.append(self.format_param_field(param))
output.append("")
# Methods
if len(info.public_methods) > 0:
output.append("### Methods")
for method in info.public_methods:
output.append(f"#### {method.name}")
output.append(self.format_method_signature(method.name, method.parameters))
output.append("")
if method.parameters and len(method.parameters) > 0:
output.append("**Parameters:**")
output.append("")
for param in method.parameters:
output.append(self.format_param_field(param))
output.append("")
return "\n".join(output)
def collect_public_classes() -> list[type]:
public_names = getattr(opik_optimizer, "__all__", [])
classes: list[type] = []
seen: set[int] = set()
for name in public_names:
obj = getattr(opik_optimizer, name, None)
if inspect.isclass(obj) and id(obj) not in seen:
classes.append(obj)
seen.add(id(obj))
# PromptLibrary is a public customization surface but not re-exported at top-level.
from opik_optimizer.utils.prompt_library import PromptLibrary
if id(PromptLibrary) not in seen:
classes.append(PromptLibrary)
return classes
classes_to_document = collect_public_classes()
res = """---
title: "Opik Agent Optimizer API Reference"
subtitle: "Technical SDK reference guide"
---
The Opik Agent Optimizer SDK provides a comprehensive set of tools for optimizing LLM prompts and agents. This reference guide documents the standardized API that all optimizers follow, ensuring consistency and interoperability across different optimization algorithms.
## Key Features
- **Standardized API**: All optimizers follow the same interface for `optimize_prompt()` methods
- **Multiple Algorithms**: Support for various optimization strategies including evolutionary, few-shot, meta-prompt, and GEPA
- **MCP Support**: Built-in support for Model Context Protocol tool calling and optimization
- **Consistent Results**: All optimizers return standardized `OptimizationResult` objects
- **Counter Tracking**: Built-in LLM and tool call counters for monitoring usage
- **Backward Compatibility**: All original parameters preserved through kwargs extraction
- **Deprecation Warnings**: Clear warnings for deprecated parameters with migration guidance
## Core Classes
The SDK provides several optimizer classes that all inherit from `BaseOptimizer` and implement the same standardized interface:
- **ParameterOptimizer**: Optimizes LLM call parameters (temperature, top_p, etc.) using Bayesian optimization
- **FewShotBayesianOptimizer**: Uses few-shot learning with Bayesian optimization
- **MetaPromptOptimizer**: Employs meta-prompting techniques for optimization
- **EvolutionaryOptimizer**: Uses genetic algorithms for prompt evolution
- **GepaOptimizer**: Leverages GEPA (Genetic-Pareto) optimization approach
- **HRPO (Hierarchical Reflective Prompt Optimizer)**: Uses hierarchical root cause analysis for targeted prompt refinement
## Standardized Method Signatures
All optimizers implement these core methods with identical signatures:
### optimize_prompt()
```python
def optimize_prompt(
self,
prompt: ChatPrompt | dict[str, ChatPrompt],
dataset: Dataset,
metric: MetricFunction,
agent: OptimizableAgent | None = None,
experiment_config: dict | None = None,
n_samples: int | None = None,
auto_continue: bool = False,
project_name: str | None = None,
optimization_id: str | None = None,
validation_dataset: Dataset | None = None,
max_trials: int = 10,
allow_tool_use: bool = True,
**kwargs: Any,
) -> OptimizationResult
```
## Deprecation Warnings
The following parameters are deprecated and will be removed in future versions:
### Constructor Parameters
- **`num_threads`** in optimizer constructors: Use `n_threads` instead
### Example Migration
```python
# ❌ Deprecated
optimizer = FewShotBayesianOptimizer(
model="gpt-4o-mini",
num_threads=16, # Deprecated
)
# ✅ Correct
optimizer = FewShotBayesianOptimizer(
model="gpt-4o-mini",
n_threads=16, # Use n_threads instead
)
```
"""
for class_obj in classes_to_document:
inspector = ClassInspector(class_obj)
res += f"{inspector.to_render_string()}\n"
def main() -> None:
parser = argparse.ArgumentParser(
description="Generate Fern documentation for Opik Optimizer API"
)
parser.add_argument(
"--write",
action="store_true",
help="Write output to Fern docs file (default: print to stdout)",
)
args = parser.parse_args()
if args.write:
# Path to Fern docs
script_dir = Path(__file__).parent
repo_root = script_dir.parent.parent.parent # Go up to repo root
fern_docs_path = (
repo_root
/ "apps"
/ "opik-documentation"
/ "documentation"
/ "fern"
/ "docs"
/ "agent_optimization"
/ "opik_optimizer"
/ "reference.mdx"
)
# Verify the file exists before overwriting
if not fern_docs_path.exists():
print(f"⚠️ Warning: Target file does not exist: {fern_docs_path}")
print("Creating new file...")
fern_docs_path.parent.mkdir(parents=True, exist_ok=True)
# Write the file
fern_docs_path.write_text(res, encoding="utf-8")
print(f"✅ Documentation written to: {fern_docs_path}")
else:
print(res)
if __name__ == "__main__":
main()