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

Add header to Codex API requests for capturing product analytics data #53

Merged
merged 11 commits into from
Mar 7, 2025
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.0.2] - 2025-03-03

- Pass metadata in headers for query requests.

## [1.0.1] - 2025-02-26

- Updates to logic for `is_unhelpful_response` util method.
Expand All @@ -15,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initial release of the `cleanlab-codex` client library.

[Unreleased]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.1...HEAD
[Unreleased]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.2...HEAD
[1.0.2]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/cleanlab/cleanlab-codex/compare/267a93300f77c94e215d7697223931e7926cad9e...v1.0.0
2 changes: 1 addition & 1 deletion src/cleanlab_codex/__about__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: MIT
__version__ = "1.0.1"
__version__ = "1.0.2"
7 changes: 6 additions & 1 deletion src/cleanlab_codex/codex_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing_extensions import Annotated

from cleanlab_codex.project import Project
from cleanlab_codex.utils.analytics import AnalyticsMetadata
from cleanlab_codex.utils.errors import MissingDependencyError
from cleanlab_codex.utils.function import (
pydantic_model_from_function,
Expand Down Expand Up @@ -110,7 +111,11 @@ def query(
Returns:
The answer to the question if available. If no answer is available, this returns a fallback answer or None.
"""
return self._project.query(question, fallback_answer=self._fallback_answer)[0]
return self._project.query(
question=question,
fallback_answer=self._fallback_answer,
analytics_metadata=AnalyticsMetadata(integration_type="tool"),
)[0]

def to_openai_tool(self) -> dict[str, Any]:
"""Converts the tool to the expected format for an [OpenAI function tool](https://platform.openai.com/docs/guides/function-calling).
Expand Down
40 changes: 0 additions & 40 deletions src/cleanlab_codex/internal/project.py

This file was deleted.

40 changes: 35 additions & 5 deletions src/cleanlab_codex/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

from codex import AuthenticationError

from cleanlab_codex.internal.project import query_project
from cleanlab_codex.internal.sdk_client import client_from_access_key
from cleanlab_codex.types.entry import Entry
from cleanlab_codex.types.project import ProjectConfig
from cleanlab_codex.utils.analytics import AnalyticsMetadata

if _TYPE_CHECKING:
from datetime import datetime

from codex import Codex as _Codex

from cleanlab_codex.types.entry import Entry, EntryCreate
from cleanlab_codex.types.entry import EntryCreate

_ERROR_CREATE_ACCESS_KEY = (
"Failed to create access key. Please ensure you have the necessary permissions "
Expand Down Expand Up @@ -159,6 +160,7 @@ def query(
*,
fallback_answer: Optional[str] = None,
read_only: bool = False,
analytics_metadata: Optional[AnalyticsMetadata] = None,
) -> tuple[Optional[str], Optional[Entry]]:
"""Query Codex to check if this project contains an answer to the question. Add the question to the project for SME review if it does not.

Expand All @@ -173,10 +175,38 @@ def query(
If Codex is able to answer the question, the first element will be the answer returned by Codex and the second element will be the existing [`Entry`](/codex/api/python/types.entry#class-entry) in the Codex project.
If Codex is unable to answer the question, the first element will be `fallback_answer` if provided, otherwise None. The second element will be a new [`Entry`](/codex/api/python/types.entry#class-entry) in the Codex project.
"""
return query_project(
client=self._sdk_client,
if not analytics_metadata:
analytics_metadata = AnalyticsMetadata(integration_type="backup")

return self._query_project(
question=question,
project_id=self.id,
fallback_answer=fallback_answer,
read_only=read_only,
analytics_metadata=analytics_metadata,
)

def _query_project(
self,
question: str,
*,
fallback_answer: Optional[str] = None,
read_only: bool = False,
analytics_metadata: Optional[AnalyticsMetadata] = None,
) -> tuple[Optional[str], Optional[Entry]]:
extra_headers = analytics_metadata.to_headers() if analytics_metadata else None
maybe_entry = self._sdk_client.projects.entries.query(self._id, question=question, extra_headers=extra_headers)

if maybe_entry is not None:
entry = Entry.model_validate(maybe_entry.model_dump())
if entry.answer is not None:
return entry.answer, entry

return fallback_answer, entry

if not read_only:
created_entry = Entry.model_validate(
self._sdk_client.projects.entries.add_question(self._id, question=question).model_dump()
)
return fallback_answer, created_entry

return fallback_answer, None
19 changes: 19 additions & 0 deletions src/cleanlab_codex/utils/analytics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import annotations

from cleanlab_codex.__about__ import __version__ as package_version


class AnalyticsMetadata:
def __init__(
self, *, integration_type: str, package_version: str = package_version, source: str = "cleanlab-codex-python"
):
self._integration_type = integration_type
self._package_version = package_version
self._source = source

def to_headers(self) -> dict[str, str]:
return {
"X-Integration-Type": self._integration_type,
"X-Client-Library-Version": self._package_version,
"X-Source": self._source,
}
Loading