Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helicone helpers async #3367

Merged
merged 3 commits into from
Mar 4, 2025
Merged

Helicone helpers async #3367

merged 3 commits into from
Mar 4, 2025

Conversation

chitalian
Copy link
Contributor

No description provided.

Copy link

vercel bot commented Mar 4, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
helicone 🔄 Building (Inspect) Visit Preview 💬 Add feedback Mar 4, 2025 0:18am
helicone-bifrost 🔄 Building (Inspect) Visit Preview 💬 Add feedback Mar 4, 2025 0:18am
helicone-eu 🔄 Building (Inspect) Visit Preview 💬 Add feedback Mar 4, 2025 0:18am

@chitalian chitalian merged commit 0f0d565 into main Mar 4, 2025
4 of 9 checks passed
@chitalian chitalian deleted the helicone-helpers-async branch March 4, 2025 00:18
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR introduces significant updates to Helicone's manual logging functionality, focusing on improved async support and enhanced logging capabilities across Python and TypeScript SDKs.

  • Changed default logging endpoint to api.worker.helicone.ai and added time-to-first-token tracking in both Python and TypeScript SDKs
  • Added support for string responses via new textBody field in ProviderResponse type
  • Introduced LoggingOptions TypedDict in Python SDK for better type safety and timing control
  • Fixed TogetherAI stream processor error messages and added Kafka message handling based on KAFKA_ENABLED flag
  • Critical bug: log_request method calls non-existent __send_log after rename to send_log in Python SDK

12 file(s) reviewed, 11 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 224 to 225
# Create a streaming response
response = client.chat.completions.create(**request)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Request object is accessed directly instead of using result_recorder.request as shown in the basic example

Comment on lines +367 to +370
"stream_options": {
"include_usage": True
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: stream_options with include_usage is not a documented OpenAI parameter and may not work as expected

Suggested change
"stream_options": {
"include_usage": True
}
}
}

helicone_logger.send_log(
provider="openai",
request=request_body,
response="\n".join(chunks), # Join chunks or process as needed
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Joining chunks with newlines may produce invalid JSON structure for the response

Comment on lines +65 to +66
request={"model": "gpt-4", "messages": [...]},
response={"choices": [...]}, # Can also be a string for text responses
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: example uses ellipsis [...] which may confuse users - should show actual message structure

Suggested change
request={"model": "gpt-4", "messages": [...]},
response={"choices": [...]}, # Can also be a string for text responses
request={"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]},
response={"choices": [{"message": {"content": "Hi there!"}}]}, # Can also be a string for text responses

"content": "Write a story about a robot"
}
],
"stream": true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: stream: true in request but response uses textBody instead of streaming format

@@ -506,3 +506,4 @@ export async function POST(request: Request) {
```

For a comprehensive guide on using the Manual Logger with streaming functionality, check out our [Manual Logger with Streaming](/guides/cookbooks/manual-logger-streaming) cookbook.
````
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Extra backticks at end of file causing formatting issue

Suggested change
````

response: dict,
options: dict
response: Union[dict, str],
options: LoggingOptions
):
start_time = options.get("start_time")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Potential error: options.get() used with TypedDict which doesn't support .get() - should use direct attribute access instead

@@ -109,7 +118,8 @@ def __send_log(
"endTime": {
"seconds": int(end_time),
"milliseconds": int((end_time - int(end_time)) * 1000)
}
},
"timeToFirstToken": options.get("time_to_first_token") if options.get("time_to_first_token") is not None else None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Inconsistent naming: time_to_first_token vs time_to_first_token_ms in LoggingOptions type definition

Suggested change
"timeToFirstToken": options.get("time_to_first_token") if options.get("time_to_first_token") is not None else None
"timeToFirstToken": options.get("time_to_first_token_ms") if options.get("time_to_first_token_ms") is not None else None

Comment on lines +125 to 154
if (KAFKA_ENABLED) {
const kafkaProducer = new KafkaProducer();

if (res.error) {
Sentry.captureException(new Error(res.error), {
tags: {
type: "KafkaError",
topic: "request-response-logs-prod-dlq",
},
extra: {
requestId: logMessage.log.request.id,
responseId: logMessage.log.response.id,
orgId: handlerContext.orgParams?.id ?? "",
batchId: batchContext.batchId,
partition: batchContext.partition,
offset: batchContext.lastOffset,
messageCount: batchContext.messageCount,
},
});

console.error(
`Error sending message to DLQ: ${res.error} for request ${logMessage.log.request.id} in batch ${batchContext.batchId}`
const res = await kafkaProducer.sendMessages(
[logMessage],
"request-response-logs-prod-dlq"
);

if (res.error) {
Sentry.captureException(new Error(res.error), {
tags: {
type: "KafkaError",
topic: "request-response-logs-prod-dlq",
},
extra: {
requestId: logMessage.log.request.id,
responseId: logMessage.log.response.id,
orgId: handlerContext.orgParams?.id ?? "",
batchId: batchContext.batchId,
partition: batchContext.partition,
offset: batchContext.lastOffset,
messageCount: batchContext.messageCount,
},
});

console.error(
`Error sending message to DLQ: ${res.error} for request ${logMessage.log.request.id} in batch ${batchContext.batchId}`
);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider reusing KafkaProducer instance instead of creating new one for each error case

Comment on lines +213 to 234
if (KAFKA_ENABLED) {
const kafkaProducer = new KafkaProducer();
const kafkaResult = await kafkaProducer.sendMessages(
logMessages,
"request-response-logs-prod-dlq"
);

if (kafkaResult.error) {
Sentry.captureException(new Error(kafkaResult.error), {
tags: {
type: "KafkaError",
topic: "request-response-logs-prod-dlq",
},
extra: {
batchId: batchContext.batchId,
partition: batchContext.partition,
offset: batchContext.lastOffset,
messageCount: batchContext.messageCount,
},
});
if (kafkaResult.error) {
Sentry.captureException(new Error(kafkaResult.error), {
tags: {
type: "KafkaError",
topic: "request-response-logs-prod-dlq",
},
extra: {
batchId: batchContext.batchId,
partition: batchContext.partition,
offset: batchContext.lastOffset,
messageCount: batchContext.messageCount,
},
});
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Similar pattern of KafkaProducer instantiation - consider extracting producer creation and message sending to a reusable method

Copy link
Contributor

promptless bot commented Mar 4, 2025

📝 Documentation updates detected! You can review documentation updates here

Copy link

fumedev bot commented Mar 4, 2025

Summary

  • Introduces asynchronous logging capabilities in Helicone helpers for Python and TypeScript.
  • Adds a timeToFirstToken field to the logging options for better performance tracking.
  • Allows logging of both JSON and string responses directly via the send_log method.
  • Updates the default logging endpoint to https://api.worker.helicone.ai.
  • Includes comprehensive examples for usage across various scenarios, including streaming responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant