Skip to content

spanforge.sdk.feedback — User Feedback Client

Module: spanforge.sdk.feedback
Added in: 2.0.12 (Phase 13 — RAG Tracing & User Feedback)
Import: from spanforge.sdk import sf_feedback or from spanforge.sdk.feedback import SFFeedbackClient

spanforge.sdk.feedback provides structured collection of user feedback for LLM responses. It supports thumbs, star rating, Likert scale, and free-text modalities. Raw comment text and user identifiers are never stored; only SHA-256 hashes are retained.


Quick example

from spanforge.sdk import sf_feedback

# Thumbs-up feedback
feedback_id = sf_feedback.submit(
    session_id="sess-abc",
    trace_id="trace-xyz",
    rating="thumbs_up",
)

# Free-text feedback (raw text is hashed, not stored)
feedback_id = sf_feedback.submit(
    session_id="sess-abc",
    trace_id="trace-xyz",
    rating="free_text",
    comment="The answer was very helpful.",
    user_id="user-42",
)

# Get all feedback for a session
records = sf_feedback.get_feedback("sess-abc")

# Aggregate summary
summary = sf_feedback.get_summary("sess-abc")
print(summary.total_feedback)   # 2
print(summary.positive_count)   # 1

Singleton

spanforge.sdk.sf_feedback is a module-level SFFeedbackClient instance:

from spanforge.sdk import sf_feedback

To construct a custom instance:

from spanforge.sdk.feedback import SFFeedbackClient
from spanforge.sdk._base import SFClientConfig

client = SFFeedbackClient(SFClientConfig(api_key="..."))

Security model

DataStored as
Free-text commentNever stored — SHA-256 hash only (comment_hash)
user_idNever stored — SHA-256 hash only (user_id_hash)
ratingStored as a FeedbackRating enum value string
session_id / trace_idStored as-is — should not contain PII

FeedbackStatusInfo

@dataclass
class FeedbackStatusInfo:
    status: str
    total_submitted: int
    sessions_tracked: int

Returned by SFFeedbackClient.get_status().

FieldDescription
status"ok" or "degraded"
total_submittedTotal feedback records submitted in this process lifetime
sessions_trackedNumber of distinct session IDs that have feedback

SFFeedbackClient

class SFFeedbackClient(SFServiceClient)

Thread-safe user feedback service client.

Constructor

SFFeedbackClient(config: SFClientConfig)

submit(session_id, trace_id, rating, *, comment=None, user_id=None, source="api", metadata=None, linked_trust_dimension=None) -> str

Submit user feedback for an LLM response.

ParameterTypeDefaultDescription
session_idstr(required)Session or conversation the feedback belongs to.
trace_idstr(required)Trace ID of the specific LLM call being rated.
ratingstr | FeedbackRating(required)A FeedbackRating value or its string equivalent (e.g. "thumbs_up", "star_4", "likert_3").
commentstr | NoneNoneOptional free-text comment. SHA-256 hashed — raw text is NOT stored.
user_idstr | NoneNoneOptional user identifier. SHA-256 hashed before storage.
sourcestr"api"Feedback channel label (e.g. "api", "ui", "email").
metadatadict | NoneNoneArbitrary key-value metadata.
linked_trust_dimensionstr | NoneNoneOptional T.R.U.S.T. dimension to link this feedback to. Valid values: "transparency", "reliability", "user_trust", "security", "traceability".

Returns: str — the unique feedback_id (ULID) for the submitted record.

Emits: llm.feedback.submitted event.


get_feedback(session_id, *, rating_filter=None) -> list[dict]

Return feedback records for a session.

ParameterTypeDefaultDescription
session_idstr(required)Session to query.
rating_filterstr | FeedbackRating | NoneNoneFilter by rating type (e.g. "thumbs_up"). Returns all records when omitted.

Returns: list[dict] — feedback records in submission order (the dict form of FeedbackSubmittedPayload).


get_summary(session_id) -> FeedbackSummaryPayload

Return an aggregated feedback summary for a session.

Returns: FeedbackSummaryPayload with total_feedback, positive_count, negative_count, neutral_count, avg_numeric_score, and free_text_count.


link_to_trust(feedback_id, trust_dimension, *, session_id="") -> bool

Link an existing feedback record to a T.R.U.S.T. dimension.

ParameterTypeDescription
feedback_idstrThe feedback_id returned by submit().
trust_dimensionstrT.R.U.S.T. dimension key: "transparency", "reliability", "user_trust", "security", or "traceability".
session_idstrSession scope for event routing.

Returns: True if the link event was emitted successfully, False otherwise.

Raises: ValueError — if trust_dimension is not a valid T.R.U.S.T. dimension.


get_status() -> FeedbackStatusInfo

Return service health and feedback statistics.

status = sf_feedback.get_status()
print(status.status)            # "ok"
print(status.total_submitted)   # 27
print(status.sessions_tracked)  # 5

T.R.U.S.T. integration

Feedback can be linked to any of the five T.R.U.S.T. dimensions using linked_trust_dimension at submission time, or by calling link_to_trust() after the fact:

# At submission time
sf_feedback.submit(
    session_id="sess-abc",
    trace_id="trace-xyz",
    rating="thumbs_down",
    linked_trust_dimension="reliability",
)

# Post-submission linkage
sf_feedback.link_to_trust(
    feedback_id=fb_id,
    trust_dimension="user_trust",
    session_id="sess-abc",
)

HTTP endpoint — POST /v1/feedback

Added in: 2.0.14 (F-21)

When the embedded spanforge HTTP server is running (spanforge serve), user feedback can be submitted directly via REST without importing the SDK.

Request

POST /v1/feedback
Content-Type: application/json

Body fields:

FieldTypeRequiredDescription
session_idstrSession / conversation identifier.
trace_idstrTrace ID of the specific LLM call being rated.
ratingint | strNumeric rating 15, or a FeedbackRating string.
commentstrFree-text comment. Hashed before storage.
user_idstrUser identifier. Hashed before storage.
sourcestrFeedback source label (default "api").
metadatadictArbitrary key-value metadata.
linked_trust_dimensionstrT.R.U.S.T. dimension link ("reliability", etc.).

Response

HTTP 201 Created

{
  "feedback_id": "01JXXXXXXXXXXXXXXXXXX",
  "accepted": true
}

Example

curl -X POST http://localhost:7654/v1/feedback \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess-abc",
    "trace_id": "trace-xyz",
    "rating": 4,
    "comment": "Very helpful response.",
    "user_id": "user-42",
    "linked_trust_dimension": "reliability"
  }'
# → {"feedback_id": "01JXXXXXXXXXXXXXXXXXX", "accepted": true}

Related