agent-learning-kit 0.1.0__py3-none-any.whl
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.
- agent_learning_kit-0.1.0.dist-info/METADATA +381 -0
- agent_learning_kit-0.1.0.dist-info/RECORD +642 -0
- agent_learning_kit-0.1.0.dist-info/WHEEL +4 -0
- agent_learning_kit-0.1.0.dist-info/entry_points.txt +5 -0
- agent_learning_kit-0.1.0.dist-info/licenses/LICENSE +173 -0
- agent_learning_kit-0.1.0.dist-info/licenses/NOTICE +7 -0
- fi/__init__.py +5 -0
- fi/alk/__init__.py +57 -0
- fi/alk/_facade.py +31 -0
- fi/alk/_module_alias.py +68 -0
- fi/alk/_paths.py +14 -0
- fi/alk/_schema.py +522 -0
- fi/alk/actions.py +727 -0
- fi/alk/bench/__init__.py +517 -0
- fi/alk/bench/_codeexec.py +213 -0
- fi/alk/bench/_coding.py +215 -0
- fi/alk/bench/_docker.py +237 -0
- fi/alk/bench/_grader.py +286 -0
- fi/alk/bench/_pull.py +212 -0
- fi/alk/bench/_voice.py +147 -0
- fi/alk/capabilities.py +627 -0
- fi/alk/cli.py +6396 -0
- fi/alk/config.py +130 -0
- fi/alk/cua_loop.py +562 -0
- fi/alk/evals.py +2351 -0
- fi/alk/extensions.py +163 -0
- fi/alk/harness/ARCHITECTURE.md +231 -0
- fi/alk/harness/DESIGN.md +246 -0
- fi/alk/harness/ENVIRONMENT_CONFORMANCE.md +127 -0
- fi/alk/harness/HOW-IT-WORKS.md +297 -0
- fi/alk/harness/IMPLEMENTATION_AND_VALIDATION_STATUS.md +229 -0
- fi/alk/harness/README.md +417 -0
- fi/alk/harness/__init__.py +77 -0
- fi/alk/harness/__main__.py +3 -0
- fi/alk/harness/amend.py +312 -0
- fi/alk/harness/artifacts.py +319 -0
- fi/alk/harness/authoring_entrypoint.py +189 -0
- fi/alk/harness/authoring_runtime_validation.py +267 -0
- fi/alk/harness/backends/README.md +43 -0
- fi/alk/harness/backends/__init__.py +122 -0
- fi/alk/harness/backends/base.py +241 -0
- fi/alk/harness/backends/claude.py +211 -0
- fi/alk/harness/backends/files.py +182 -0
- fi/alk/harness/backends/vertex_gemini.py +457 -0
- fi/alk/harness/background_noise.py +95 -0
- fi/alk/harness/build.py +385 -0
- fi/alk/harness/bundle.py +593 -0
- fi/alk/harness/bundle_author_v2.py +1831 -0
- fi/alk/harness/bundle_v2.py +719 -0
- fi/alk/harness/call_runner.py +1440 -0
- fi/alk/harness/callback_http_adapter.py +111 -0
- fi/alk/harness/catalogue.py +287 -0
- fi/alk/harness/chat.py +428 -0
- fi/alk/harness/chat_call_runner.py +506 -0
- fi/alk/harness/checks.py +136 -0
- fi/alk/harness/cli.py +1354 -0
- fi/alk/harness/config.py +338 -0
- fi/alk/harness/contract.py +718 -0
- fi/alk/harness/credentials.py +674 -0
- fi/alk/harness/data/persona_vocabulary.json +111 -0
- fi/alk/harness/environment.py +99 -0
- fi/alk/harness/environment_plan.py +168 -0
- fi/alk/harness/events.py +125 -0
- fi/alk/harness/executor.py +304 -0
- fi/alk/harness/folder.py +234 -0
- fi/alk/harness/generated_runtime.py +815 -0
- fi/alk/harness/github.py +72 -0
- fi/alk/harness/hosted_authoring_entrypoint.py +183 -0
- fi/alk/harness/hosted_entrypoint.py +2402 -0
- fi/alk/harness/hosted_scheduler.py +2218 -0
- fi/alk/harness/job.py +426 -0
- fi/alk/harness/judge.py +184 -0
- fi/alk/harness/livekit_source.py +50 -0
- fi/alk/harness/livekit_tool_trace_bootstrap.py +71 -0
- fi/alk/harness/observability.py +208 -0
- fi/alk/harness/outbound.py +3252 -0
- fi/alk/harness/packaging.py +515 -0
- fi/alk/harness/persona_guides.py +157 -0
- fi/alk/harness/platform.py +692 -0
- fi/alk/harness/process_preflight.py +764 -0
- fi/alk/harness/process_runtime.py +5670 -0
- fi/alk/harness/prove.py +425 -0
- fi/alk/harness/provider_import.py +703 -0
- fi/alk/harness/provider_lifecycle.py +392 -0
- fi/alk/harness/provision.py +2896 -0
- fi/alk/harness/reception.py +147 -0
- fi/alk/harness/retell_chat_call_runner.py +373 -0
- fi/alk/harness/run/__init__.py +296 -0
- fi/alk/harness/run/alk.py +184 -0
- fi/alk/harness/run/call.py +162 -0
- fi/alk/harness/run/conversation.py +264 -0
- fi/alk/harness/run/data/voices_by_language_and_gender.json +693 -0
- fi/alk/harness/run/evidence.py +195 -0
- fi/alk/harness/run/grade.py +598 -0
- fi/alk/harness/run/live.py +297 -0
- fi/alk/harness/run/models.py +56 -0
- fi/alk/harness/run/platform_evals.py +227 -0
- fi/alk/harness/run/sdk_voice.py +130 -0
- fi/alk/harness/run/simulation.py +1209 -0
- fi/alk/harness/run/stage.py +91 -0
- fi/alk/harness/run/targets.py +508 -0
- fi/alk/harness/run/tools.py +601 -0
- fi/alk/harness/run/voice.py +340 -0
- fi/alk/harness/runtime.py +172 -0
- fi/alk/harness/sandbox_server.py +2011 -0
- fi/alk/harness/sandbox_worker.py +44 -0
- fi/alk/harness/scenario.py +1048 -0
- fi/alk/harness/scenario_source.py +879 -0
- fi/alk/harness/scenario_tools.py +1143 -0
- fi/alk/harness/scenarios.py +915 -0
- fi/alk/harness/secrets.py +168 -0
- fi/alk/harness/service_catalog.py +97 -0
- fi/alk/harness/session.py +391 -0
- fi/alk/harness/sessions.py +372 -0
- fi/alk/harness/simulator.py +76 -0
- fi/alk/harness/simulator_voice.py +928 -0
- fi/alk/harness/skills/build-environment/SKILL.md +538 -0
- fi/alk/harness/skills/harness.md +131 -0
- fi/alk/harness/skills/kinds/chat.md +48 -0
- fi/alk/harness/skills/kinds/voice-voicemail.md +63 -0
- fi/alk/harness/skills/kinds/voice.md +59 -0
- fi/alk/harness/skills/plan-suite/SKILL.md +103 -0
- fi/alk/harness/skills/provision-environment/SKILL.md +136 -0
- fi/alk/harness/skills/run-scenarios/SKILL.md +112 -0
- fi/alk/harness/skills/understand-agent/SKILL.md +251 -0
- fi/alk/harness/skills/write-scenarios/SKILL.md +606 -0
- fi/alk/harness/skills/write-scenarios/references/refusals.md +28 -0
- fi/alk/harness/skills/write-scenarios/references/world-api.md +92 -0
- fi/alk/harness/source_data_invariants.py +444 -0
- fi/alk/harness/source_tool_evidence.py +79 -0
- fi/alk/harness/sources.py +253 -0
- fi/alk/harness/spend.py +140 -0
- fi/alk/harness/tool_trace_proxy.py +104 -0
- fi/alk/harness/tools.py +1018 -0
- fi/alk/harness/understand.py +169 -0
- fi/alk/harness/voicemail_audio.py +74 -0
- fi/alk/harness/world/__init__.py +33 -0
- fi/alk/harness/world/errors.py +68 -0
- fi/alk/harness/world/expectations.py +91 -0
- fi/alk/harness/world/handle.py +538 -0
- fi/alk/harness/world/kinds.py +196 -0
- fi/alk/harness/world/mutate.py +186 -0
- fi/alk/harness/world/probe.py +413 -0
- fi/alk/harness/world/provision.py +511 -0
- fi/alk/harness/world/provisioned.py +191 -0
- fi/alk/harness/world/runtime.py +616 -0
- fi/alk/harness/world/snapshot.py +288 -0
- fi/alk/harness/world/stores/__init__.py +305 -0
- fi/alk/harness/world/stores/container.py +215 -0
- fi/alk/harness/world/stores/inprocess.py +346 -0
- fi/alk/harness/world/stores/postgres.py +481 -0
- fi/alk/harness/world/stores/prove.py +202 -0
- fi/alk/harness/world/stores/sqlite.py +245 -0
- fi/alk/harness/world/stores/written.py +182 -0
- fi/alk/harness/world/tools.py +1516 -0
- fi/alk/harness/world/workspace.py +144 -0
- fi/alk/image_loop.py +453 -0
- fi/alk/image_perturb.py +241 -0
- fi/alk/improve.py +274 -0
- fi/alk/live/__init__.py +154 -0
- fi/alk/live/_attribution.py +184 -0
- fi/alk/live/_capture.py +264 -0
- fi/alk/live/_codec.py +391 -0
- fi/alk/live/_contract.py +134 -0
- fi/alk/live/_loopback.py +316 -0
- fi/alk/live/_perturb.py +449 -0
- fi/alk/live/_runner.py +386 -0
- fi/alk/live/_stats.py +561 -0
- fi/alk/live/_transcript.py +240 -0
- fi/alk/live/_workers/__init__.py +9 -0
- fi/alk/live/_workers/a2a_worker.py +316 -0
- fi/alk/live/_workers/langgraph_worker.py +217 -0
- fi/alk/live/_workers/livekit_worker.py +207 -0
- fi/alk/live/_workers/mcp_loopback_server.py +46 -0
- fi/alk/live/_workers/mcp_worker.py +158 -0
- fi/alk/live/_workers/pipecat_worker.py +189 -0
- fi/alk/live/a2a_lane.py +138 -0
- fi/alk/live/langgraph_lane.py +339 -0
- fi/alk/live/livekit_lane.py +376 -0
- fi/alk/live/mcp_lane.py +172 -0
- fi/alk/live/pipecat_lane.py +341 -0
- fi/alk/live/voice_redteam.py +494 -0
- fi/alk/loss.py +306 -0
- fi/alk/optimize.py +36260 -0
- fi/alk/practice/__init__.py +51 -0
- fi/alk/practice/_assess.py +103 -0
- fi/alk/practice/_budget.py +81 -0
- fi/alk/practice/_calibrate.py +69 -0
- fi/alk/practice/_capstone.py +86 -0
- fi/alk/practice/_contract.py +91 -0
- fi/alk/practice/_diagnose.py +79 -0
- fi/alk/practice/_drill.py +196 -0
- fi/alk/practice/_experiment.py +720 -0
- fi/alk/practice/_schedule.py +102 -0
- fi/alk/practice/_store.py +194 -0
- fi/alk/practice/_trainer.py +245 -0
- fi/alk/practice/_update.py +125 -0
- fi/alk/redteam.py +2621 -0
- fi/alk/rewardhack.py +237 -0
- fi/alk/simulate.py +10351 -0
- fi/alk/studio/__init__.py +82 -0
- fi/alk/studio/_bias.py +314 -0
- fi/alk/studio/_calibration.py +522 -0
- fi/alk/studio/_coverage.py +262 -0
- fi/alk/studio/_download.py +665 -0
- fi/alk/studio/_fidelity_attack.py +114 -0
- fi/alk/studio/_generate.py +652 -0
- fi/alk/studio/_library.py +370 -0
- fi/alk/studio/_scan.py +134 -0
- fi/alk/studio/_upgrade.py +42 -0
- fi/alk/studio/_vendor.py +172 -0
- fi/alk/suite.py +4200 -0
- fi/alk/tasks.py +828 -0
- fi/alk/telemetry/__init__.py +149 -0
- fi/alk/telemetry/_contract.py +141 -0
- fi/alk/telemetry/_emit.py +182 -0
- fi/alk/telemetry/_ledger.py +296 -0
- fi/alk/telemetry/_queue.py +127 -0
- fi/alk/telemetry/_row.py +294 -0
- fi/alk/telemetry/_run.py +233 -0
- fi/alk/telemetry/_sync.py +193 -0
- fi/alk/telemetry/_url.py +119 -0
- fi/alk/trinity.py +49397 -0
- fi/alk/voice_loop.py +174 -0
- fi/api/__init__.py +1 -0
- fi/api/auth.py +137 -0
- fi/api/types.py +29 -0
- fi/cli/__init__.py +9 -0
- fi/cli/assertions/__init__.py +25 -0
- fi/cli/assertions/conditions.py +76 -0
- fi/cli/assertions/evaluator.py +286 -0
- fi/cli/assertions/exit_codes.py +20 -0
- fi/cli/assertions/parser.py +131 -0
- fi/cli/assertions/reporter.py +194 -0
- fi/cli/commands/__init__.py +9 -0
- fi/cli/commands/config.py +165 -0
- fi/cli/commands/export.py +208 -0
- fi/cli/commands/init.py +112 -0
- fi/cli/commands/list_cmd.py +213 -0
- fi/cli/commands/run.py +486 -0
- fi/cli/commands/validate.py +173 -0
- fi/cli/commands/view.py +424 -0
- fi/cli/config/__init__.py +6 -0
- fi/cli/config/defaults.py +206 -0
- fi/cli/config/loader.py +155 -0
- fi/cli/config/schema.py +174 -0
- fi/cli/main.py +78 -0
- fi/cli/output/__init__.py +6 -0
- fi/cli/output/formatters.py +106 -0
- fi/cli/output/reporters.py +46 -0
- fi/cli/storage/__init__.py +5 -0
- fi/cli/storage/run_history.py +249 -0
- fi/cli/utils/__init__.py +5 -0
- fi/cli/utils/console.py +44 -0
- fi/evals/__init__.py +131 -0
- fi/evals/autoeval/__init__.py +137 -0
- fi/evals/autoeval/analyzer.py +211 -0
- fi/evals/autoeval/config.py +244 -0
- fi/evals/autoeval/export.py +213 -0
- fi/evals/autoeval/interactive.py +283 -0
- fi/evals/autoeval/pipeline.py +625 -0
- fi/evals/autoeval/prompts.py +139 -0
- fi/evals/autoeval/recommender.py +242 -0
- fi/evals/autoeval/rules.py +589 -0
- fi/evals/autoeval/templates.py +299 -0
- fi/evals/autoeval/types.py +232 -0
- fi/evals/core/__init__.py +16 -0
- fi/evals/core/cloud_registry.py +184 -0
- fi/evals/core/engines.py +368 -0
- fi/evals/core/evaluate.py +319 -0
- fi/evals/core/judge_prompt.py +90 -0
- fi/evals/core/prompt_generator.py +83 -0
- fi/evals/core/registry.py +57 -0
- fi/evals/core/result.py +55 -0
- fi/evals/evaluator.py +721 -0
- fi/evals/execution.py +168 -0
- fi/evals/feedback/__init__.py +32 -0
- fi/evals/feedback/calibrator.py +160 -0
- fi/evals/feedback/collector.py +214 -0
- fi/evals/feedback/hooks.py +81 -0
- fi/evals/feedback/retriever.py +128 -0
- fi/evals/feedback/store.py +272 -0
- fi/evals/feedback/types.py +99 -0
- fi/evals/framework/README.md +79 -0
- fi/evals/framework/__init__.py +267 -0
- fi/evals/framework/backends/Dockerfile.eval-runner +33 -0
- fi/evals/framework/backends/__init__.py +99 -0
- fi/evals/framework/backends/_container.py +141 -0
- fi/evals/framework/backends/_utils.py +145 -0
- fi/evals/framework/backends/base.py +223 -0
- fi/evals/framework/backends/celery_backend.py +417 -0
- fi/evals/framework/backends/celery_worker.py +78 -0
- fi/evals/framework/backends/kubernetes_backend.py +665 -0
- fi/evals/framework/backends/ray_backend.py +521 -0
- fi/evals/framework/backends/temporal.py +350 -0
- fi/evals/framework/backends/temporal_worker.py +126 -0
- fi/evals/framework/backends/thread_pool.py +286 -0
- fi/evals/framework/context.py +258 -0
- fi/evals/framework/enrichment.py +306 -0
- fi/evals/framework/evals/__init__.py +68 -0
- fi/evals/framework/evals/agentic.py +399 -0
- fi/evals/framework/evals/builder.py +609 -0
- fi/evals/framework/evals/semantic.py +142 -0
- fi/evals/framework/evaluator.py +647 -0
- fi/evals/framework/evaluators/__init__.py +22 -0
- fi/evals/framework/evaluators/blocking.py +347 -0
- fi/evals/framework/evaluators/non_blocking.py +577 -0
- fi/evals/framework/propagation.py +421 -0
- fi/evals/framework/protocols.py +385 -0
- fi/evals/framework/registry.py +370 -0
- fi/evals/framework/resilience/__init__.py +150 -0
- fi/evals/framework/resilience/circuit_breaker.py +309 -0
- fi/evals/framework/resilience/degradation.py +355 -0
- fi/evals/framework/resilience/health.py +505 -0
- fi/evals/framework/resilience/rate_limiter.py +228 -0
- fi/evals/framework/resilience/retry.py +274 -0
- fi/evals/framework/resilience/types.py +288 -0
- fi/evals/framework/resilience/wrapper.py +433 -0
- fi/evals/framework/types.py +218 -0
- fi/evals/guardrails/README.md +915 -0
- fi/evals/guardrails/__init__.py +96 -0
- fi/evals/guardrails/backends/__init__.py +43 -0
- fi/evals/guardrails/backends/azure.py +361 -0
- fi/evals/guardrails/backends/base.py +88 -0
- fi/evals/guardrails/backends/generic_llm.py +163 -0
- fi/evals/guardrails/backends/granite.py +216 -0
- fi/evals/guardrails/backends/llamaguard.py +221 -0
- fi/evals/guardrails/backends/local_base.py +479 -0
- fi/evals/guardrails/backends/openai.py +365 -0
- fi/evals/guardrails/backends/qwen.py +170 -0
- fi/evals/guardrails/backends/shieldgemma.py +154 -0
- fi/evals/guardrails/backends/turing.py +235 -0
- fi/evals/guardrails/backends/vllm_client.py +321 -0
- fi/evals/guardrails/backends/wildguard.py +188 -0
- fi/evals/guardrails/base.py +888 -0
- fi/evals/guardrails/config.py +221 -0
- fi/evals/guardrails/discovery.py +243 -0
- fi/evals/guardrails/gateway.py +437 -0
- fi/evals/guardrails/registry.py +231 -0
- fi/evals/guardrails/scanners/__init__.py +127 -0
- fi/evals/guardrails/scanners/base.py +191 -0
- fi/evals/guardrails/scanners/code_injection.py +243 -0
- fi/evals/guardrails/scanners/eval_delegate.py +574 -0
- fi/evals/guardrails/scanners/invisible_chars.py +351 -0
- fi/evals/guardrails/scanners/jailbreak.py +412 -0
- fi/evals/guardrails/scanners/language.py +288 -0
- fi/evals/guardrails/scanners/pipeline.py +260 -0
- fi/evals/guardrails/scanners/regex.py +311 -0
- fi/evals/guardrails/scanners/secrets.py +274 -0
- fi/evals/guardrails/scanners/topics.py +649 -0
- fi/evals/guardrails/scanners/urls.py +341 -0
- fi/evals/guardrails/types.py +96 -0
- fi/evals/llm/__init__.py +3 -0
- fi/evals/llm/base_llm_provider.py +35 -0
- fi/evals/llm/providers/litellm.py +70 -0
- fi/evals/local/__init__.py +90 -0
- fi/evals/local/evaluator.py +690 -0
- fi/evals/local/execution_mode.py +121 -0
- fi/evals/local/llm.py +489 -0
- fi/evals/local/metrics/__init__.py +19 -0
- fi/evals/local/registry.py +360 -0
- fi/evals/manager.py +1018 -0
- fi/evals/manager_types.py +362 -0
- fi/evals/metrics/__init__.py +185 -0
- fi/evals/metrics/agents/__init__.py +74 -0
- fi/evals/metrics/agents/metrics.py +693 -0
- fi/evals/metrics/agents/report.py +36463 -0
- fi/evals/metrics/agents/types.py +160 -0
- fi/evals/metrics/base_llm_metric.py +111 -0
- fi/evals/metrics/base_metric.py +138 -0
- fi/evals/metrics/code_security/__init__.py +305 -0
- fi/evals/metrics/code_security/analyzer.py +985 -0
- fi/evals/metrics/code_security/benchmarks/__init__.py +73 -0
- fi/evals/metrics/code_security/benchmarks/builtin.py +750 -0
- fi/evals/metrics/code_security/benchmarks/loader.py +580 -0
- fi/evals/metrics/code_security/benchmarks/types.py +308 -0
- fi/evals/metrics/code_security/detectors/__init__.py +186 -0
- fi/evals/metrics/code_security/detectors/base.py +394 -0
- fi/evals/metrics/code_security/detectors/cryptography.py +345 -0
- fi/evals/metrics/code_security/detectors/injection.py +744 -0
- fi/evals/metrics/code_security/detectors/secrets.py +287 -0
- fi/evals/metrics/code_security/detectors/serialization.py +192 -0
- fi/evals/metrics/code_security/joint_metrics.py +588 -0
- fi/evals/metrics/code_security/judges/__init__.py +83 -0
- fi/evals/metrics/code_security/judges/base.py +238 -0
- fi/evals/metrics/code_security/judges/dual_judge.py +534 -0
- fi/evals/metrics/code_security/judges/llm_judge.py +301 -0
- fi/evals/metrics/code_security/judges/pattern_judge.py +515 -0
- fi/evals/metrics/code_security/metrics.py +388 -0
- fi/evals/metrics/code_security/modes/__init__.py +63 -0
- fi/evals/metrics/code_security/modes/adversarial.py +284 -0
- fi/evals/metrics/code_security/modes/autocomplete.py +198 -0
- fi/evals/metrics/code_security/modes/base.py +283 -0
- fi/evals/metrics/code_security/modes/instruct.py +253 -0
- fi/evals/metrics/code_security/modes/repair.py +230 -0
- fi/evals/metrics/code_security/reports/__init__.py +57 -0
- fi/evals/metrics/code_security/reports/generator.py +404 -0
- fi/evals/metrics/code_security/reports/leaderboard.py +509 -0
- fi/evals/metrics/code_security/types.py +534 -0
- fi/evals/metrics/function_calling/__init__.py +34 -0
- fi/evals/metrics/function_calling/metrics.py +573 -0
- fi/evals/metrics/function_calling/types.py +87 -0
- fi/evals/metrics/hallucination/__init__.py +54 -0
- fi/evals/metrics/hallucination/detector.py +149 -0
- fi/evals/metrics/hallucination/metrics.py +390 -0
- fi/evals/metrics/hallucination/nli.py +253 -0
- fi/evals/metrics/hallucination/sentinel.py +106 -0
- fi/evals/metrics/hallucination/types.py +132 -0
- fi/evals/metrics/heuristics/aggregation_metrics.py +85 -0
- fi/evals/metrics/heuristics/json_metrics.py +87 -0
- fi/evals/metrics/heuristics/similarity_metrics.py +375 -0
- fi/evals/metrics/heuristics/string_metrics.py +391 -0
- fi/evals/metrics/llm_as_judges/__init__.py +17 -0
- fi/evals/metrics/llm_as_judges/custom_judge/metric.py +112 -0
- fi/evals/metrics/llm_as_judges/custom_judge/prompts.py +26 -0
- fi/evals/metrics/llm_as_judges/types.py +48 -0
- fi/evals/metrics/rag/__init__.py +111 -0
- fi/evals/metrics/rag/advanced/__init__.py +14 -0
- fi/evals/metrics/rag/advanced/multi_hop.py +283 -0
- fi/evals/metrics/rag/advanced/source_attribution.py +344 -0
- fi/evals/metrics/rag/generation/__init__.py +17 -0
- fi/evals/metrics/rag/generation/answer_relevancy.py +176 -0
- fi/evals/metrics/rag/generation/context_utilization.py +245 -0
- fi/evals/metrics/rag/generation/faithfulness.py +241 -0
- fi/evals/metrics/rag/generation/groundedness.py +131 -0
- fi/evals/metrics/rag/rag_score.py +277 -0
- fi/evals/metrics/rag/retrieval/__init__.py +20 -0
- fi/evals/metrics/rag/retrieval/context_entity_recall.py +124 -0
- fi/evals/metrics/rag/retrieval/context_precision.py +158 -0
- fi/evals/metrics/rag/retrieval/context_recall.py +106 -0
- fi/evals/metrics/rag/retrieval/noise_sensitivity.py +163 -0
- fi/evals/metrics/rag/retrieval/ranking.py +261 -0
- fi/evals/metrics/rag/types.py +100 -0
- fi/evals/metrics/rag/utils/__init__.py +62 -0
- fi/evals/metrics/rag/utils/claims.py +189 -0
- fi/evals/metrics/rag/utils/entities.py +244 -0
- fi/evals/metrics/rag/utils/nli.py +92 -0
- fi/evals/metrics/rag/utils/similarity.py +345 -0
- fi/evals/metrics/structured/__init__.py +114 -0
- fi/evals/metrics/structured/field_completeness.py +313 -0
- fi/evals/metrics/structured/hierarchy_score.py +366 -0
- fi/evals/metrics/structured/json_validation.py +190 -0
- fi/evals/metrics/structured/schema_compliance.py +280 -0
- fi/evals/metrics/structured/structured_output_score.py +298 -0
- fi/evals/metrics/structured/types.py +108 -0
- fi/evals/metrics/structured/validators/__init__.py +30 -0
- fi/evals/metrics/structured/validators/base.py +189 -0
- fi/evals/metrics/structured/validators/json_validator.py +196 -0
- fi/evals/metrics/structured/validators/pydantic_validator.py +178 -0
- fi/evals/metrics/structured/validators/yaml_validator.py +248 -0
- fi/evals/otel/__init__.py +266 -0
- fi/evals/otel/config.py +400 -0
- fi/evals/otel/conventions.py +463 -0
- fi/evals/otel/enrichment.py +371 -0
- fi/evals/otel/instrumentors/__init__.py +140 -0
- fi/evals/otel/instrumentors/anthropic.py +517 -0
- fi/evals/otel/instrumentors/base.py +382 -0
- fi/evals/otel/instrumentors/openai.py +673 -0
- fi/evals/otel/processors/__init__.py +36 -0
- fi/evals/otel/processors/base.py +473 -0
- fi/evals/otel/processors/cost.py +445 -0
- fi/evals/otel/processors/evaluation.py +559 -0
- fi/evals/otel/processors/llm.py +462 -0
- fi/evals/otel/tracer.py +506 -0
- fi/evals/otel/types.py +232 -0
- fi/evals/otel_utils.py +23 -0
- fi/evals/protect.py +671 -0
- fi/evals/protect_input_adapter.py +154 -0
- fi/evals/streaming/__init__.py +88 -0
- fi/evals/streaming/buffer.py +213 -0
- fi/evals/streaming/evaluator.py +551 -0
- fi/evals/streaming/policy.py +307 -0
- fi/evals/streaming/scorers.py +368 -0
- fi/evals/streaming/types.py +238 -0
- fi/evals/templates.py +472 -0
- fi/evals/types.py +156 -0
- fi/opt/__init__.py +221 -0
- fi/opt/_objective_scoring.py +85 -0
- fi/opt/base/__init__.py +11 -0
- fi/opt/base/base_generator.py +33 -0
- fi/opt/base/base_mapper.py +26 -0
- fi/opt/base/base_optimizer.py +45 -0
- fi/opt/base/evaluator.py +211 -0
- fi/opt/components.py +3095 -0
- fi/opt/datamappers/__init__.py +3 -0
- fi/opt/datamappers/basic_mapper.py +40 -0
- fi/opt/deployment.py +1021 -0
- fi/opt/evidence.py +4332 -0
- fi/opt/generators/__init__.py +3 -0
- fi/opt/generators/litellm.py +66 -0
- fi/opt/integrations/__init__.py +23 -0
- fi/opt/integrations/generative_suite.py +410 -0
- fi/opt/integrations/simulate.py +1313 -0
- fi/opt/mutations.py +771 -0
- fi/opt/observability.py +4639 -0
- fi/opt/optimizer_trace.py +889 -0
- fi/opt/optimizers/__init__.py +80 -0
- fi/opt/optimizers/agent.py +331 -0
- fi/opt/optimizers/agent_bandit.py +392 -0
- fi/opt/optimizers/agent_curriculum.py +635 -0
- fi/opt/optimizers/agent_evolution.py +894 -0
- fi/opt/optimizers/agent_feedback.py +1863 -0
- fi/opt/optimizers/agent_pareto.py +547 -0
- fi/opt/optimizers/agent_social_memory.py +1113 -0
- fi/opt/optimizers/agent_tpe.py +321 -0
- fi/opt/optimizers/bayesian_search.py +449 -0
- fi/opt/optimizers/council.py +2075 -0
- fi/opt/optimizers/futureagi_replay.py +799 -0
- fi/opt/optimizers/gepa.py +322 -0
- fi/opt/optimizers/metaprompt.py +243 -0
- fi/opt/optimizers/promptwizard.py +417 -0
- fi/opt/optimizers/protegi.py +329 -0
- fi/opt/optimizers/random_search.py +224 -0
- fi/opt/research.py +518 -0
- fi/opt/simulation.py +260 -0
- fi/opt/targets.py +232 -0
- fi/opt/types.py +66 -0
- fi/opt/utils/__init__.py +4 -0
- fi/opt/utils/early_stopping.py +266 -0
- fi/opt/utils/setup_logging.py +82 -0
- fi/simulate/__init__.py +540 -0
- fi/simulate/_hashing.py +35 -0
- fi/simulate/_logging.py +10 -0
- fi/simulate/adapters.py +87 -0
- fi/simulate/agent/__init__.py +120 -0
- fi/simulate/agent/browser.py +658 -0
- fi/simulate/agent/definition.py +587 -0
- fi/simulate/agent/frameworks.py +3528 -0
- fi/simulate/agent/generic.py +8286 -0
- fi/simulate/agent/import_probe.py +227 -0
- fi/simulate/agent/memory.py +905 -0
- fi/simulate/agent/mocks.py +101 -0
- fi/simulate/agent/multi_agent.py +361 -0
- fi/simulate/agent/orchestration.py +903 -0
- fi/simulate/agent/realtime.py +665 -0
- fi/simulate/agent/wrapper.py +99 -0
- fi/simulate/agent/wrappers/__init__.py +18 -0
- fi/simulate/agent/wrappers/anthropic.py +62 -0
- fi/simulate/agent/wrappers/gemini.py +65 -0
- fi/simulate/agent/wrappers/http.py +404 -0
- fi/simulate/agent/wrappers/langchain.py +80 -0
- fi/simulate/agent/wrappers/openai.py +75 -0
- fi/simulate/agent/wrappers/websocket.py +326 -0
- fi/simulate/artifacts/__init__.py +11 -0
- fi/simulate/artifacts/manifest.py +62 -0
- fi/simulate/cli.py +20560 -0
- fi/simulate/endpoints/__init__.py +45 -0
- fi/simulate/endpoints/_http_actor.py +73 -0
- fi/simulate/endpoints/actor_sources.py +243 -0
- fi/simulate/endpoints/base.py +107 -0
- fi/simulate/endpoints/builtins.py +10 -0
- fi/simulate/endpoints/callable.py +95 -0
- fi/simulate/endpoints/http.py +76 -0
- fi/simulate/endpoints/livekit.py +138 -0
- fi/simulate/endpoints/originators.py +132 -0
- fi/simulate/endpoints/profiles.py +348 -0
- fi/simulate/endpoints/retell.py +633 -0
- fi/simulate/endpoints/vapi.py +205 -0
- fi/simulate/endpoints/websocket.py +76 -0
- fi/simulate/environment.py +33026 -0
- fi/simulate/environments/__init__.py +11 -0
- fi/simulate/environments/base.py +73 -0
- fi/simulate/environments/chat.py +697 -0
- fi/simulate/environments/voice.py +212 -0
- fi/simulate/evaluation/__init__.py +4 -0
- fi/simulate/evaluation/ai_eval.py +227 -0
- fi/simulate/evidence/__init__.py +35 -0
- fi/simulate/evidence/base.py +59 -0
- fi/simulate/evidence/caller_observed.py +50 -0
- fi/simulate/evidence/livekit_instrumentation.py +51 -0
- fi/simulate/evidence/livekit_room.py +50 -0
- fi/simulate/evidence/otel.py +49 -0
- fi/simulate/evidence/providers/__init__.py +24 -0
- fi/simulate/evidence/providers/base.py +61 -0
- fi/simulate/evidence/providers/retell.py +376 -0
- fi/simulate/evidence/providers/vapi.py +426 -0
- fi/simulate/hosted/__init__.py +32 -0
- fi/simulate/hosted/child_entrypoint.py +306 -0
- fi/simulate/hosted/job.py +150 -0
- fi/simulate/hosted/targets.py +53 -0
- fi/simulate/instrumentation/__init__.py +5 -0
- fi/simulate/instrumentation/livekit/__init__.py +122 -0
- fi/simulate/manifest.py +1033 -0
- fi/simulate/matrix_cli.py +165 -0
- fi/simulate/realtime/__init__.py +40 -0
- fi/simulate/realtime/events.py +107 -0
- fi/simulate/realtime/media.py +61 -0
- fi/simulate/realtime/session.py +91 -0
- fi/simulate/recording/__init__.py +5 -0
- fi/simulate/recording/room_recorder.py +326 -0
- fi/simulate/registry.py +185 -0
- fi/simulate/results/__init__.py +9 -0
- fi/simulate/results/base.py +18 -0
- fi/simulate/results/filesystem.py +71 -0
- fi/simulate/results/futureagi.py +1340 -0
- fi/simulate/runtime/__init__.py +85 -0
- fi/simulate/runtime/capabilities.py +40 -0
- fi/simulate/runtime/events.py +63 -0
- fi/simulate/runtime/failures.py +25 -0
- fi/simulate/runtime/ids.py +34 -0
- fi/simulate/runtime/plan.py +70 -0
- fi/simulate/runtime/planner.py +102 -0
- fi/simulate/runtime/report.py +174 -0
- fi/simulate/runtime/run.py +75 -0
- fi/simulate/runtime/runner.py +333 -0
- fi/simulate/runtime/spec.py +186 -0
- fi/simulate/simulation/__init__.py +30 -0
- fi/simulate/simulation/behavior_policy.py +425 -0
- fi/simulate/simulation/bridge/__init__.py +9 -0
- fi/simulate/simulation/bridge/audio.py +29 -0
- fi/simulate/simulation/bridge/connector.py +46 -0
- fi/simulate/simulation/bridge/livekit.py +252 -0
- fi/simulate/simulation/bridge/retell.py +188 -0
- fi/simulate/simulation/bridge/vapi.py +177 -0
- fi/simulate/simulation/contract.py +419 -0
- fi/simulate/simulation/engines/__init__.py +12 -0
- fi/simulate/simulation/engines/base.py +21 -0
- fi/simulate/simulation/engines/cloud.py +517 -0
- fi/simulate/simulation/engines/livekit.py +4167 -0
- fi/simulate/simulation/engines/local_text.py +89 -0
- fi/simulate/simulation/fidelity.py +374 -0
- fi/simulate/simulation/gemini_tts_stream.py +110 -0
- fi/simulate/simulation/generator.py +91 -0
- fi/simulate/simulation/goal_machine.py +185 -0
- fi/simulate/simulation/livekit_models.py +467 -0
- fi/simulate/simulation/matrix.py +170 -0
- fi/simulate/simulation/models.py +279 -0
- fi/simulate/simulation/runner.py +153 -0
- fi/simulate/simulation/synthetic.py +880 -0
- fi/simulate/simulation/voice_prompt.py +502 -0
- fi/simulate/simulator/__init__.py +55 -0
- fi/simulate/simulator/builtins.py +53 -0
- fi/simulate/suite.py +1288 -0
- fi/simulate/utils/routes.py +164 -0
- fi/simulate/voice.py +225 -0
- fi/simulate/voice_cli.py +182 -0
- fi/utils/__init__.py +1 -0
- fi/utils/constants.py +14 -0
- fi/utils/errors.py +200 -0
- fi/utils/executor.py +26 -0
- fi/utils/routes.py +119 -0
- fi/utils/utils.py +17 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import random
|
|
4
|
+
import re
|
|
5
|
+
from typing import Any, Dict, List, Optional, Set
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..base.base_optimizer import BaseOptimizer
|
|
10
|
+
from ..datamappers.basic_mapper import BasicDataMapper
|
|
11
|
+
from ..base.evaluator import Evaluator
|
|
12
|
+
from ..generators.litellm import LiteLLMGenerator
|
|
13
|
+
from ..types import IterationHistory, OptimizationResult
|
|
14
|
+
from ..utils.early_stopping import EarlyStoppingConfig, EarlyStoppingChecker
|
|
15
|
+
|
|
16
|
+
GET_GRADIENTS_PROMPT = """
|
|
17
|
+
You are an expert in prompt engineering. I'm trying to write a zero-shot classifier prompt.
|
|
18
|
+
My current prompt is:
|
|
19
|
+
---
|
|
20
|
+
{prompt}
|
|
21
|
+
---
|
|
22
|
+
This prompt performed poorly on the following examples:
|
|
23
|
+
---
|
|
24
|
+
{error_examples}
|
|
25
|
+
---
|
|
26
|
+
Provide {num_feedbacks} distinct reasons why the prompt could have failed. Each reason should be a concise critique.
|
|
27
|
+
Return ONLY a valid JSON object with a single key "variations" containing a list of strings (the critiques).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
APPLY_GRADIENT_PROMPT = """
|
|
31
|
+
You are an expert in prompt engineering. I'm trying to improve a zero-shot classifier prompt.
|
|
32
|
+
My current prompt is:
|
|
33
|
+
---
|
|
34
|
+
{prompt}
|
|
35
|
+
---
|
|
36
|
+
It performed poorly on these examples:
|
|
37
|
+
---
|
|
38
|
+
{error_examples}
|
|
39
|
+
---
|
|
40
|
+
A key reason for the failure is the following critique: "{feedback}"
|
|
41
|
+
Based on this critique, generate {num_new_prompts} different, improved versions of the prompt.
|
|
42
|
+
Return ONLY a valid JSON object with a single key "variations" containing a list of strings (the new prompts).
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
PARAPHRASE_PROMPT = """
|
|
46
|
+
Generate {num_variations} semantic paraphrases of the following prompt. The meaning should be identical, but the wording should be different.
|
|
47
|
+
---
|
|
48
|
+
{prompt}
|
|
49
|
+
---
|
|
50
|
+
Return ONLY a valid JSON object with a single key "variations" containing a list of strings (the paraphrased prompts).
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class GradientVariations(BaseModel):
|
|
55
|
+
variations: List[str] = Field(description="A list of generated text strings.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ProTeGi(BaseOptimizer):
|
|
59
|
+
"""
|
|
60
|
+
A corrected and robust implementation of the ProTeGi optimizer.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
teacher_generator: LiteLLMGenerator,
|
|
66
|
+
num_gradients: int = 4,
|
|
67
|
+
errors_per_gradient: int = 4,
|
|
68
|
+
prompts_per_gradient: int = 1,
|
|
69
|
+
beam_size: int = 4,
|
|
70
|
+
task_model: Optional[str] = None,
|
|
71
|
+
):
|
|
72
|
+
self.teacher = teacher_generator
|
|
73
|
+
self.task_model = task_model
|
|
74
|
+
self.num_gradients = num_gradients
|
|
75
|
+
self.errors_per_gradient = errors_per_gradient
|
|
76
|
+
self.prompts_per_gradient = prompts_per_gradient
|
|
77
|
+
self.beam_size = beam_size
|
|
78
|
+
logging.info("--- ProTeGi Optimizer Initialized ---")
|
|
79
|
+
|
|
80
|
+
def optimize(
|
|
81
|
+
self,
|
|
82
|
+
evaluator: Evaluator,
|
|
83
|
+
data_mapper: BasicDataMapper,
|
|
84
|
+
dataset: List[Dict[str, Any]],
|
|
85
|
+
initial_prompts: List[str],
|
|
86
|
+
early_stopping: Optional[EarlyStoppingConfig] = None,
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> OptimizationResult:
|
|
89
|
+
num_rounds = kwargs.get("num_rounds", 3)
|
|
90
|
+
eval_subset_size = kwargs.get("eval_subset_size", 32)
|
|
91
|
+
|
|
92
|
+
# Initialize early stopping checker
|
|
93
|
+
checker = None
|
|
94
|
+
if early_stopping and early_stopping.is_enabled():
|
|
95
|
+
checker = EarlyStoppingChecker(early_stopping)
|
|
96
|
+
logging.info(f"Early stopping enabled: {early_stopping}")
|
|
97
|
+
|
|
98
|
+
beam = set(initial_prompts)
|
|
99
|
+
best_overall_score = -1.0
|
|
100
|
+
best_overall_prompt = initial_prompts[0] if initial_prompts else ""
|
|
101
|
+
history: List[IterationHistory] = []
|
|
102
|
+
|
|
103
|
+
for round_num in range(num_rounds):
|
|
104
|
+
logging.info(
|
|
105
|
+
f"\n--- Starting Optimization Round {round_num + 1}/{num_rounds} ---"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# 1. EXPANSION: Generate new candidates from the current beam
|
|
109
|
+
current_prompts = list(beam)
|
|
110
|
+
logging.info(
|
|
111
|
+
f"Expanding {len(current_prompts)} prompts into new candidates..."
|
|
112
|
+
)
|
|
113
|
+
expanded_prompts = self._expand_candidates(
|
|
114
|
+
current_prompts, evaluator, data_mapper, dataset
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# The candidate pool for this round is the union of the old beam and new prompts
|
|
118
|
+
candidate_pool = beam.union(expanded_prompts)
|
|
119
|
+
logging.info(
|
|
120
|
+
f"Candidate pool for this round has {len(candidate_pool)} unique prompts."
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# 2. SELECTION: Score all candidates in the pool
|
|
124
|
+
eval_subset = random.sample(dataset, min(len(dataset), eval_subset_size))
|
|
125
|
+
iteration_history = self._score_candidates(
|
|
126
|
+
list(candidate_pool), evaluator, data_mapper, eval_subset
|
|
127
|
+
)
|
|
128
|
+
history.extend(iteration_history)
|
|
129
|
+
|
|
130
|
+
# 3. BEAM UPDATE: Select the top N prompts for the next round
|
|
131
|
+
sorted_history = sorted(
|
|
132
|
+
iteration_history, key=lambda x: x.average_score, reverse=True
|
|
133
|
+
)
|
|
134
|
+
if not sorted_history:
|
|
135
|
+
logging.warning("No successful evaluations in this round. Halting.")
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
beam = {item.prompt for item in sorted_history[: self.beam_size]}
|
|
139
|
+
best_round_score = sorted_history[0].average_score
|
|
140
|
+
best_round_prompt = sorted_history[0].prompt
|
|
141
|
+
|
|
142
|
+
logging.info(f"Best score in round {round_num + 1}: {best_round_score:.4f}")
|
|
143
|
+
logging.info(f"New beam selected with {len(beam)} prompts.")
|
|
144
|
+
|
|
145
|
+
if best_round_score > best_overall_score:
|
|
146
|
+
best_overall_score = best_round_score
|
|
147
|
+
best_overall_prompt = best_round_prompt
|
|
148
|
+
|
|
149
|
+
# Check early stopping
|
|
150
|
+
if checker:
|
|
151
|
+
num_evals = len(candidate_pool) * len(eval_subset)
|
|
152
|
+
if checker.should_stop(best_round_score, num_evals):
|
|
153
|
+
logging.info(
|
|
154
|
+
f"Early stopping triggered: {checker.get_state()['stop_reason']}"
|
|
155
|
+
)
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
final_best_generator = LiteLLMGenerator(
|
|
159
|
+
self.teacher.model_name, best_overall_prompt
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Build result with early stopping metadata
|
|
163
|
+
return OptimizationResult(
|
|
164
|
+
best_generator=final_best_generator,
|
|
165
|
+
history=history,
|
|
166
|
+
final_score=best_overall_score,
|
|
167
|
+
early_stopped=checker.get_state()["stopped"] if checker else False,
|
|
168
|
+
stop_reason=checker.get_state()["stop_reason"] if checker else None,
|
|
169
|
+
total_iterations=len(history),
|
|
170
|
+
total_evaluations=(
|
|
171
|
+
checker.get_state()["total_evaluations"]
|
|
172
|
+
if checker
|
|
173
|
+
else sum(len(h.individual_results) for h in history)
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _expand_candidates(
|
|
178
|
+
self,
|
|
179
|
+
prompts: List[str],
|
|
180
|
+
evaluator: Evaluator,
|
|
181
|
+
data_mapper: BasicDataMapper,
|
|
182
|
+
dataset: List[Dict[str, Any]],
|
|
183
|
+
) -> Set[str]:
|
|
184
|
+
new_prompts = set()
|
|
185
|
+
for i, prompt in enumerate(prompts):
|
|
186
|
+
logging.debug(f"--> Expanding prompt {i + 1}/{len(prompts)}...")
|
|
187
|
+
errors = self._get_errors(prompt, evaluator, data_mapper, dataset)
|
|
188
|
+
if not errors:
|
|
189
|
+
logging.debug("Prompt produced no errors. No expansion.")
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
critiques = self._get_gradients(prompt, errors)
|
|
193
|
+
logging.debug(f"Generated {len(critiques)} critiques (gradients).")
|
|
194
|
+
|
|
195
|
+
for feedback in critiques:
|
|
196
|
+
generated = self._apply_gradient(prompt, errors, feedback)
|
|
197
|
+
if generated:
|
|
198
|
+
logging.debug(
|
|
199
|
+
f"Generated {len(generated)} new prompts from critique: '{feedback[:50]}...'"
|
|
200
|
+
)
|
|
201
|
+
new_prompts.update(generated)
|
|
202
|
+
return new_prompts
|
|
203
|
+
|
|
204
|
+
def _get_errors(
|
|
205
|
+
self,
|
|
206
|
+
prompt: str,
|
|
207
|
+
evaluator: Evaluator,
|
|
208
|
+
data_mapper: BasicDataMapper,
|
|
209
|
+
dataset: List[Dict[str, Any]],
|
|
210
|
+
sample_size: int = 32,
|
|
211
|
+
) -> List[Dict[str, Any]]:
|
|
212
|
+
subset = random.sample(dataset, min(len(dataset), sample_size))
|
|
213
|
+
temp_generator = LiteLLMGenerator(
|
|
214
|
+
self.task_model or self.teacher.model_name, prompt
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
generated_outputs = [temp_generator.generate(example) for example in subset]
|
|
218
|
+
eval_inputs = [
|
|
219
|
+
data_mapper.map(gen_out, ex)
|
|
220
|
+
for gen_out, ex in zip(generated_outputs, subset)
|
|
221
|
+
]
|
|
222
|
+
results = evaluator.evaluate(eval_inputs)
|
|
223
|
+
|
|
224
|
+
errors = [subset[i] for i, res in enumerate(results) if res.score < 0.5]
|
|
225
|
+
logging.debug(
|
|
226
|
+
f"Found {len(errors)} errors with score < 0.5 from a subset of {len(subset)}."
|
|
227
|
+
)
|
|
228
|
+
return errors
|
|
229
|
+
|
|
230
|
+
def _get_gradients(self, prompt: str, errors: List[Dict[str, Any]]) -> List[str]:
|
|
231
|
+
error_sample = random.sample(errors, min(len(errors), self.errors_per_gradient))
|
|
232
|
+
critique_prompt = GET_GRADIENTS_PROMPT.format(
|
|
233
|
+
prompt=prompt,
|
|
234
|
+
error_examples=json.dumps(error_sample, indent=2, ensure_ascii=False),
|
|
235
|
+
num_feedbacks=self.num_gradients,
|
|
236
|
+
)
|
|
237
|
+
response_text = self.teacher.generate(
|
|
238
|
+
prompt_vars={"prompt": critique_prompt},
|
|
239
|
+
response_format={"type": "json_object"},
|
|
240
|
+
)
|
|
241
|
+
return self._parse_variations_from_json(response_text)
|
|
242
|
+
|
|
243
|
+
def _apply_gradient(
|
|
244
|
+
self, prompt: str, errors: List[Dict[str, Any]], feedback: str
|
|
245
|
+
) -> List[str]:
|
|
246
|
+
error_sample = random.sample(errors, min(len(errors), self.errors_per_gradient))
|
|
247
|
+
rewrite_prompt = APPLY_GRADIENT_PROMPT.format(
|
|
248
|
+
prompt=prompt,
|
|
249
|
+
error_examples=json.dumps(error_sample, indent=2, ensure_ascii=False),
|
|
250
|
+
feedback=feedback,
|
|
251
|
+
num_new_prompts=self.prompts_per_gradient,
|
|
252
|
+
)
|
|
253
|
+
response_text = self.teacher.generate(
|
|
254
|
+
prompt_vars={"prompt": rewrite_prompt},
|
|
255
|
+
response_format={"type": "json_object"},
|
|
256
|
+
)
|
|
257
|
+
return self._parse_variations_from_json(response_text)
|
|
258
|
+
|
|
259
|
+
def _score_candidates(
|
|
260
|
+
self,
|
|
261
|
+
prompts: List[str],
|
|
262
|
+
evaluator: Evaluator,
|
|
263
|
+
data_mapper: BasicDataMapper,
|
|
264
|
+
dataset: List[Dict[str, Any]],
|
|
265
|
+
) -> List[IterationHistory]:
|
|
266
|
+
histories = []
|
|
267
|
+
for i, prompt in enumerate(prompts):
|
|
268
|
+
logging.info(
|
|
269
|
+
f"--> Scoring prompt {i + 1}/{len(prompts)}: '{prompt[:100]}...'"
|
|
270
|
+
)
|
|
271
|
+
temp_generator = LiteLLMGenerator(
|
|
272
|
+
self.task_model or self.teacher.model_name, prompt
|
|
273
|
+
)
|
|
274
|
+
generated_outputs = [
|
|
275
|
+
temp_generator.generate(example) for example in dataset
|
|
276
|
+
]
|
|
277
|
+
eval_inputs = [
|
|
278
|
+
data_mapper.map(gen_out, ex)
|
|
279
|
+
for gen_out, ex in zip(generated_outputs, dataset)
|
|
280
|
+
]
|
|
281
|
+
results = evaluator.evaluate(eval_inputs)
|
|
282
|
+
avg_score = (
|
|
283
|
+
sum(res.score for res in results) / len(results) if results else 0.0
|
|
284
|
+
)
|
|
285
|
+
logging.info(f" Average score: {avg_score:.4f}")
|
|
286
|
+
histories.append(
|
|
287
|
+
IterationHistory(
|
|
288
|
+
prompt=prompt, average_score=avg_score, individual_results=results
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
return histories
|
|
292
|
+
|
|
293
|
+
@staticmethod
|
|
294
|
+
def _parse_variations_from_json(text: str) -> List[str]:
|
|
295
|
+
text = text.strip()
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
data = json.loads(text)
|
|
299
|
+
return GradientVariations.model_validate(data).variations
|
|
300
|
+
except (json.JSONDecodeError, ValidationError):
|
|
301
|
+
pass
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
match = re.search(r"```json\s*(\{.*?\})\s*```", text, re.DOTALL)
|
|
305
|
+
if match:
|
|
306
|
+
json_str = match.group(1)
|
|
307
|
+
data = json.loads(json_str)
|
|
308
|
+
return GradientVariations.model_validate(data).variations
|
|
309
|
+
except (json.JSONDecodeError, ValidationError):
|
|
310
|
+
pass
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
start_index = text.find("{")
|
|
314
|
+
end_index = text.rfind("}")
|
|
315
|
+
if start_index != -1 and end_index != -1 and end_index > start_index:
|
|
316
|
+
json_str = text[start_index : end_index + 1]
|
|
317
|
+
data = json.loads(json_str)
|
|
318
|
+
return GradientVariations.model_validate(data).variations
|
|
319
|
+
except (json.JSONDecodeError, ValidationError) as e:
|
|
320
|
+
logging.error(
|
|
321
|
+
f"Failed to parse teacher model JSON response after all fallbacks: {e}"
|
|
322
|
+
)
|
|
323
|
+
logging.debug(f"Raw problematic output that failed parsing:\n{text}")
|
|
324
|
+
return []
|
|
325
|
+
|
|
326
|
+
# If no JSON object is found at all
|
|
327
|
+
logging.warning("Could not find any JSON in the teacher's response.")
|
|
328
|
+
logging.debug(f"Raw response with no JSON:\n{text}")
|
|
329
|
+
return []
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import litellm
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
6
|
+
from typing import List, Dict, Any, Optional
|
|
7
|
+
|
|
8
|
+
from ..base.evaluator import Evaluator
|
|
9
|
+
from ..base.base_optimizer import BaseOptimizer
|
|
10
|
+
from ..base.base_generator import BaseGenerator
|
|
11
|
+
|
|
12
|
+
from ..datamappers import BasicDataMapper
|
|
13
|
+
from ..types import IterationHistory, OptimizationResult
|
|
14
|
+
from ..utils.early_stopping import EarlyStoppingConfig, EarlyStoppingChecker
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PromptVariations(BaseModel):
|
|
20
|
+
"""Output model for prompt variations generated by the teacher model."""
|
|
21
|
+
|
|
22
|
+
variations: List[str] = Field(
|
|
23
|
+
description="A list of prompt variations.",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RandomSearchOptimizer(BaseOptimizer):
|
|
28
|
+
"""
|
|
29
|
+
A simple optimization strategy that tries a number of random prompt variations
|
|
30
|
+
generated by a powerful "teacher" model.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
generator: BaseGenerator,
|
|
36
|
+
teacher_model: str = "gpt-5",
|
|
37
|
+
num_variations: int = 5,
|
|
38
|
+
teacher_model_kwargs: Dict[str, Any] = {},
|
|
39
|
+
eval_template: str = "summary_quality",
|
|
40
|
+
eval_model_name: str = "turing_flash",
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Initializes the Random Search Optimizer.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
generator: The generator to be optimized.
|
|
47
|
+
teacher_model: A powerful LLM to generate prompt variations.
|
|
48
|
+
num_variations: The number of random variations to try.
|
|
49
|
+
teacher_model_kwargs: A dictionary of kwargs to pass to the teacher model.
|
|
50
|
+
eval_template: The evaluation template to use from the ai-evaluation library.
|
|
51
|
+
eval_model_name: The model to use for evaluation.
|
|
52
|
+
"""
|
|
53
|
+
self.generator = generator
|
|
54
|
+
self.teacher_model = teacher_model
|
|
55
|
+
self.num_variations = num_variations
|
|
56
|
+
|
|
57
|
+
if teacher_model_kwargs is None and "gpt" in teacher_model:
|
|
58
|
+
self.teacher_model_kwargs = {"temperature": 1.0, "max_tokens": 16000}
|
|
59
|
+
else:
|
|
60
|
+
self.teacher_model_kwargs = teacher_model_kwargs or {}
|
|
61
|
+
|
|
62
|
+
super().__init__()
|
|
63
|
+
|
|
64
|
+
def optimize(
|
|
65
|
+
self,
|
|
66
|
+
evaluator: Evaluator,
|
|
67
|
+
data_mapper: BasicDataMapper,
|
|
68
|
+
dataset: List[Dict[str, Any]],
|
|
69
|
+
early_stopping: Optional[EarlyStoppingConfig] = None,
|
|
70
|
+
**kwargs: Any,
|
|
71
|
+
) -> OptimizationResult:
|
|
72
|
+
logger.info("--- Starting Random Search Optimization ---")
|
|
73
|
+
optimization_start_time = time.time()
|
|
74
|
+
|
|
75
|
+
# Initialize early stopping checker
|
|
76
|
+
checker = None
|
|
77
|
+
if early_stopping and early_stopping.is_enabled():
|
|
78
|
+
checker = EarlyStoppingChecker(early_stopping)
|
|
79
|
+
logger.info(f"Early stopping enabled: {early_stopping}")
|
|
80
|
+
|
|
81
|
+
initial_prompt = self.generator.get_prompt_template()
|
|
82
|
+
best_prompt = initial_prompt
|
|
83
|
+
best_score = -1.0
|
|
84
|
+
history = []
|
|
85
|
+
|
|
86
|
+
variations = self._generate_variations(initial_prompt)
|
|
87
|
+
|
|
88
|
+
for i, variation in enumerate(variations):
|
|
89
|
+
iteration_start_time = time.time()
|
|
90
|
+
logger.info(f"--- Testing Variation {i + 1}/{len(variations)} ---")
|
|
91
|
+
logger.info(f"Prompt: {variation}")
|
|
92
|
+
self.generator.set_prompt_template(variation)
|
|
93
|
+
|
|
94
|
+
# Time generation
|
|
95
|
+
generation_start_time = time.time()
|
|
96
|
+
generated_outputs = [
|
|
97
|
+
self.generator.generate(example) for example in dataset
|
|
98
|
+
]
|
|
99
|
+
generation_end_time = time.time()
|
|
100
|
+
logger.info(
|
|
101
|
+
f"Generation for {len(dataset)} examples took {generation_end_time - generation_start_time:.2f} seconds."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
eval_inputs = [
|
|
105
|
+
data_mapper.map(gen_output, example)
|
|
106
|
+
for gen_output, example in zip(generated_outputs, dataset)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
# Time evaluation
|
|
110
|
+
evaluation_start_time = time.time()
|
|
111
|
+
evaluation_results = evaluator.evaluate(eval_inputs)
|
|
112
|
+
evaluation_end_time = time.time()
|
|
113
|
+
logger.info(
|
|
114
|
+
f"Evaluation for {len(dataset)} examples took {evaluation_end_time - evaluation_start_time:.2f} seconds."
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if not evaluation_results:
|
|
118
|
+
logger.warning(
|
|
119
|
+
"No evaluation results were returned for this variation."
|
|
120
|
+
)
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
# Calculate average score for decision-making
|
|
124
|
+
avg_score = sum(res.score for res in evaluation_results) / len(
|
|
125
|
+
evaluation_results
|
|
126
|
+
)
|
|
127
|
+
logger.info(f"Average Score for Variation {i + 1}: {avg_score:.4f}")
|
|
128
|
+
|
|
129
|
+
history.append(
|
|
130
|
+
IterationHistory(
|
|
131
|
+
prompt=variation,
|
|
132
|
+
average_score=avg_score,
|
|
133
|
+
individual_results=evaluation_results,
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Check early stopping
|
|
138
|
+
if checker:
|
|
139
|
+
num_evals = len(dataset)
|
|
140
|
+
if checker.should_stop(avg_score, num_evals):
|
|
141
|
+
logger.info(
|
|
142
|
+
f"Early stopping triggered: {checker.get_state()['stop_reason']}"
|
|
143
|
+
)
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
if avg_score > best_score:
|
|
147
|
+
best_score = avg_score
|
|
148
|
+
best_prompt = variation
|
|
149
|
+
logger.info("--- New Best Prompt Found! ---")
|
|
150
|
+
|
|
151
|
+
iteration_end_time = time.time()
|
|
152
|
+
logger.info(
|
|
153
|
+
f"--- Variation {i + 1} finished in {iteration_end_time - iteration_start_time:.2f} seconds ---"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.generator.set_prompt_template(best_prompt)
|
|
157
|
+
|
|
158
|
+
optimization_end_time = time.time()
|
|
159
|
+
logger.info(
|
|
160
|
+
f"--- Random Search Optimization finished in {optimization_end_time - optimization_start_time:.2f} seconds ---"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Build result with early stopping metadata
|
|
164
|
+
return OptimizationResult(
|
|
165
|
+
best_generator=self.generator,
|
|
166
|
+
history=history,
|
|
167
|
+
final_score=best_score,
|
|
168
|
+
early_stopped=checker.get_state()["stopped"] if checker else False,
|
|
169
|
+
stop_reason=checker.get_state()["stop_reason"] if checker else None,
|
|
170
|
+
total_iterations=len(history),
|
|
171
|
+
total_evaluations=(
|
|
172
|
+
checker.get_state()["total_evaluations"]
|
|
173
|
+
if checker
|
|
174
|
+
else len(history) * len(dataset)
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def _generate_variations(self, initial_prompt: str) -> List[str]:
|
|
179
|
+
# this entire thing should ideally be sent to the generator itself.
|
|
180
|
+
logger.info(
|
|
181
|
+
f"Generating {self.num_variations} prompt variations with teacher model: {self.teacher_model}..."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
instruction = f"""
|
|
185
|
+
You are an expert in prompt engineering. Your task is to generate {self.num_variations} variations of the following prompt.
|
|
186
|
+
The variations should be diverse and explore different styles.
|
|
187
|
+
Return ONLY a JSON object with a key "variations" containing a list of strings. For example:
|
|
188
|
+
{{"variations": ["prompt 1", "prompt 2", ...]}}
|
|
189
|
+
|
|
190
|
+
Initial Prompt:
|
|
191
|
+
---
|
|
192
|
+
{initial_prompt}
|
|
193
|
+
---
|
|
194
|
+
"""
|
|
195
|
+
messages = [{"role": "user", "content": instruction}]
|
|
196
|
+
response_content = ""
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
teacher_model_kwargs = self.teacher_model_kwargs.copy()
|
|
200
|
+
teacher_model_kwargs["response_format"] = {"type": "json_object"}
|
|
201
|
+
|
|
202
|
+
response = litellm.completion(
|
|
203
|
+
model=self.teacher_model,
|
|
204
|
+
messages=messages,
|
|
205
|
+
**teacher_model_kwargs,
|
|
206
|
+
)
|
|
207
|
+
response_content = response.choices[0].message.content
|
|
208
|
+
# logger.info(f"Teacher model response:\n{response_content}")
|
|
209
|
+
|
|
210
|
+
# Clean the response content to ensure it is valid JSON
|
|
211
|
+
# Sometimes models return JSON wrapped in ```json ... ```
|
|
212
|
+
if response_content.strip().startswith("```json"):
|
|
213
|
+
response_content = response_content.strip()[7:-3].strip()
|
|
214
|
+
|
|
215
|
+
prompt_variations = PromptVariations.model_validate_json(response_content)
|
|
216
|
+
return prompt_variations.variations
|
|
217
|
+
|
|
218
|
+
except (ValidationError, json.JSONDecodeError) as e:
|
|
219
|
+
logger.error(f"Failed to parse teacher model response: {e}")
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"An error occurred with the teacher model: {e}")
|
|
222
|
+
|
|
223
|
+
logger.error(f"Problematic response content was: {response_content}")
|
|
224
|
+
return [initial_prompt + " Be creative."]
|