Skip to content

Commit 956df36

Browse files
committed
Merge branch 'complete-refactor' of github.com:honeyhiveai/python-sdk into complete-refactor
# Conflicts: # CHANGELOG.md
2 parents c15c3fd + e328907 commit 956df36

6 files changed

Lines changed: 158 additions & 17 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
- Organized dependencies by category: core, LLM providers, instrumentors, agent frameworks
2222
- Included installation commands for each specific integration
2323
- Documentation of required environment variables per provider
24+
- **📚 Examples: Evaluation Example**
25+
- Added simple evaluation example demonstrating the `evaluate()` function
26+
- Shows basic dataset evaluation with span enrichment
27+
- Includes ground truth comparisons
28+
- **📚 Examples: Legacy SDK Example**
29+
- Added old SDK integration example for reference
30+
- Demonstrates basic tracer initialization and OpenAI integration
2431

2532
### Fixed
2633
- **🐛 CRITICAL: Fixed evaluate() + enrich_span() Pattern (v1.0 Baggage Fix)**
@@ -34,6 +41,11 @@
3441
- Added 5 unit tests for selective propagation
3542
- Added integration test for `evaluate()` + enrichment pattern
3643

44+
- **🔧 Experiments: Session Enrichment Always Runs**
45+
- Fixed `evaluate()` function to enrich sessions even when no evaluators are provided
46+
- Sessions now get outputs enriched regardless of evaluator presence
47+
- Changed session enrichment log level from debug to info for better visibility
48+
- Ensures session data is always persisted to backend
3749
- **🔧 Tracing: Restored enrich_session() Backwards Compatibility**
3850
- Fixed breaking signature changes in `enrich_session()` that removed `session_id` and `user_properties` parameters
3951
- Restored `session_id` as optional positional parameter for backwards compatibility

‎docs/changelog.rst‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ Current Version Highlights
3838
* **Installation Commands**: Per-integration pip install commands for easy setup
3939
* **Environment Variables**: Documentation of required credentials for each provider
4040

41+
**📚 ADDED: New Example Files**
42+
43+
* **Evaluation Example**: Simple demonstration of the ``evaluate()`` function with dataset evaluation and span enrichment
44+
* **Legacy SDK Example**: Reference example showing basic tracer initialization and OpenAI integration
45+
46+
**🔧 FIXED: Session Enrichment in evaluate() Function**
47+
48+
* **Always Enriches Sessions**: Fixed bug where sessions weren't enriched when no evaluators were provided
49+
* **Output Persistence**: Ensures outputs are always saved to backend regardless of evaluator presence
50+
* **Better Logging**: Upgraded log level from debug to info for session enrichment visibility
51+
4152
**🔧 IMPROVED: Tracer Internal Cleanup**
4253

4354
* **Code Simplification**: Removed redundant experiment baggage code path

‎docs/reference/index.rst‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ Experiments Module
151151

152152
**Modern evaluation framework** with decorator-based evaluators and backend-powered aggregation.
153153

154+
.. note::
155+
**Session Enrichment**: The ``evaluate()`` function always enriches sessions with outputs, regardless of whether evaluators are provided. This ensures all execution results are persisted to the backend for later analysis.
156+
154157
.. toctree::
155158
:maxdepth: 1
156159

@@ -417,6 +420,14 @@ Package Information
417420
418421
pip install honeyhive
419422
423+
**Example Files**:
424+
425+
The SDK includes example files in the ``examples/`` directory:
426+
427+
- ``eval_example.py`` - Demonstrates the ``evaluate()`` function with dataset evaluation and span enrichment
428+
- ``integrations/old_sdk.py`` - Legacy SDK example showing basic tracer initialization and OpenAI integration
429+
- ``integrations/`` - Full integration examples for various LLM providers and frameworks
430+
420431
**See Also:**
421432

422433
- :doc:`../tutorials/index` - Learn by doing

‎examples/eval_example.py‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from honeyhive import HoneyHive
2+
from honeyhive.experiments import evaluate
3+
import os
4+
from dotenv import load_dotenv
5+
from datetime import datetime
6+
from honeyhive.api import DatasetsAPI, DatapointsAPI, MetricsAPI
7+
from pydantic import BaseModel
8+
from uuid import uuid4
9+
from honeyhive.models.generated import ReturnType
10+
from honeyhive.models import (
11+
CreateDatapointRequest,
12+
CreateDatasetRequest,
13+
Metric,
14+
)
15+
from honeyhive import enrich_span
16+
17+
load_dotenv()
18+
19+
DATASET_NAME = "sample-honeyhive-9-30-25"
20+
21+
22+
def invoke_summary_agent(**kwargs):
23+
return "The American Shorthair is a pedigreed cat breed, originally known as the Domestic Shorthair, that was among the first CFA-registered breeds in 1906 and was renamed in 1966 to distinguish it from random-bred domestic short-haired cats while highlighting its American origins."
24+
25+
dataset = [
26+
{
27+
"inputs": {
28+
"context": "The Poodle, called the Pudel in German and the Caniche in French, is a breed of water dog. The breed is divided into four varieties based on size, the Standard Poodle, Medium Poodle, Miniature Poodle and Toy Poodle, although the Medium Poodle is not universally recognised. They have a distinctive thick, curly coat that comes in many colours and patterns, with only solid colours recognised by major breed registries. Poodles are active and intelligent, and are particularly able to learn from humans. Poodles tend to live 10–18 years, with smaller varieties tending to live longer than larger ones."
29+
},
30+
"ground_truths": {
31+
"result": "The Poodle is an intelligent water dog breed that comes in four size varieties with a distinctive curly coat, known for its trainability and relatively long lifespan of 10-18 years."
32+
},
33+
},
34+
{
35+
"inputs": {
36+
"context": 'The American Shorthair is a pedigree cat breed, with a strict conformation standard, as set by cat fanciers of the breed and North American cat fancier associations such as The International Cat Association (TICA) and the CFA. The breed is accepted by all North American cat registries. Originally known as the Domestic Shorthair, in 1966 the breed was renamed the American Shorthair to better represent its "all-American" origins and to differentiate it from other short-haired breeds. The name American Shorthair also reinforces the breed\'s pedigreed status as distinct from the random-bred non-pedigreed domestic short-haired cats in North America, which may nevertheless resemble the American Shorthair. Both the American Shorthair breed and the random-bred cats from which the breed is derived are sometimes called working cats because they were used for controlling rodent populations, on ships and farms. The American Shorthair (then referred to as the Domestic Shorthair) was among the first five breeds that were registered by the CFA in 1906.'
37+
},
38+
"ground_truths": {
39+
"result": "The American Shorthair is a pedigreed cat breed, originally known as the Domestic Shorthair, that was among the first CFA-registered breeds in 1906 and was renamed in 1966 to distinguish it from random-bred domestic short-haired cats while highlighting its American origins."
40+
},
41+
},
42+
]
43+
44+
45+
if __name__ == "__main__":
46+
def evaluation_function(datapoint):
47+
inputs = datapoint.get("inputs", {})
48+
context = inputs.get("context", "")
49+
enrich_span(metrics={"input_length": len(context)})
50+
return {
51+
"answer": invoke_summary_agent(**{"context": context})
52+
}
53+
54+
result = evaluate(
55+
function=evaluation_function,
56+
dataset=dataset,
57+
api_key=os.environ["HH_API_KEY"],
58+
project=os.environ["HH_PROJECT"],
59+
name=f"{DATASET_NAME}-{datetime.now().isoformat()}",
60+
verbose=True, # Enable verbose to see output enrichment
61+
)
62+
63+
print(result)

‎examples/integrations/old_sdk.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import os
2+
from flask import Flask, render_template, request, jsonify
3+
from openai import OpenAI
4+
from dotenv import load_dotenv
5+
from honeyhive import HoneyHiveTracer, trace, enrich_span
6+
load_dotenv()
7+
app = Flask(__name__)
8+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9+
# Place the code below at the beginning of your application to initialize the tracer
10+
HoneyHiveTracer.init(
11+
api_key=os.getenv("HH_API_KEY"),
12+
project="sdk", # Your HoneyHive project name
13+
source='dev', #Optional
14+
session_name='Test Session', #Optional
15+
server_url="https://api.staging.honeyhive.ai"
16+
)
17+
# Additionally, trace any function in your code using @trace / @atrace decorator
18+
@trace
19+
def call_openai(user_input):
20+
client = OpenAI()
21+
# Example: Add feedback data for HoneyHive evaluation
22+
# if user_input.strip().lower() == "what is the capital of france?":
23+
# HoneyHiveTracer.add_feedback({
24+
# "ground_truth": "The capital of France is Paris.",
25+
# "keywords": ["Paris", "France", "capital"]
26+
# })
27+
completion = client.chat.completions.create(
28+
model='gpt-4o-mini',
29+
messages=[{"role":"user","content": user_input}]
30+
)
31+
return completion.choices[0].message.content
32+
# @app.route("/")
33+
# def index():
34+
# return render_template("index.html")
35+
# @app.route("/chat", methods=["POST"])
36+
# def chat():
37+
# data = request.get_json()
38+
# user_input = data.get("message", "")
39+
# try:
40+
# reply = call_openai(user_input)
41+
# return jsonify({"reply": reply})
42+
# except Exception as e:
43+
# return jsonify({"error": str(e)}), 500
44+
# if __name__ == "__main__":
45+
# app.run(debug=True)
46+
call_openai("hi")

‎src/honeyhive/experiments/core.py‎

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,7 @@ def _enrich_session_with_results(
426426

427427
if verbose:
428428
enriched_fields = list(update_data.keys())
429-
logger.debug(
430-
"Enriched session %s with: %s", session_id, enriched_fields
431-
)
429+
logger.info("Enriched session %s with: %s", session_id, enriched_fields)
432430
except Exception as e:
433431
logger.warning("Failed to enrich session %s: %s", session_id, str(e))
434432

@@ -819,21 +817,21 @@ def evaluate( # pylint: disable=too-many-locals,too-many-branches
819817
"Evaluators complete: %d metrics collected", len(evaluator_metrics)
820818
)
821819

822-
# Enrich sessions with outputs and evaluator metrics
823-
if verbose:
824-
logger.info("Enriching sessions with outputs and evaluator metrics")
820+
# Enrich sessions with outputs and evaluator metrics (always, not just when evaluators exist)
821+
if verbose:
822+
logger.info("Enriching sessions with outputs and evaluator metrics")
825823

826-
for result in execution_results:
827-
session_id = result.get("session_id")
828-
if session_id:
829-
_enrich_session_with_results(
830-
session_id=session_id,
831-
datapoint_id=result.get("datapoint_id"),
832-
outputs=result.get("outputs"),
833-
evaluator_metrics=evaluator_metrics,
834-
client=client,
835-
verbose=verbose,
836-
)
824+
for result in execution_results:
825+
session_id = result.get("session_id")
826+
if session_id:
827+
_enrich_session_with_results(
828+
session_id=session_id,
829+
datapoint_id=result.get("datapoint_id"),
830+
outputs=result.get("outputs"),
831+
evaluator_metrics=evaluator_metrics or {},
832+
client=client,
833+
verbose=verbose,
834+
)
837835

838836
# Step 6: Update run with results
839837
_update_run_with_results(

0 commit comments

Comments
 (0)