Skip to content

Commit eda2f05

Browse files
Feature - add custom metadata property for query (#70)
* Feature - add custom metadata property for query * bump version and changelog * add test for metadata * hatch fmt * fix type * remove 'for analytics purposes'
1 parent f691ac6 commit eda2f05

File tree

7 files changed

+38
-11
lines changed

7 files changed

+38
-11
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.0.8] - 2025-04-03
11+
12+
- Update `Project.query()` method with optional `metadata` property to log and store arbitrary metadata.
1013
- Remove `response_validation.py` module.
1114

1215
## [1.0.7] - 2025-04-02
@@ -44,7 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4447

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

47-
[Unreleased]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.7...HEAD
50+
[Unreleased]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.8...HEAD
51+
[1.0.8]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.7...v1.0.8
4852
[1.0.7]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.6...v1.0.7
4953
[1.0.6]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.5...v1.0.6
5054
[1.0.5]: https://github.com/cleanlab/cleanlab-codex/compare/v1.0.4...v1.0.5

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
dependencies = [
2828
"cleanlab-tlm~=1.0.12",
29-
"codex-sdk==0.1.0-alpha.13",
29+
"codex-sdk==0.1.0-alpha.14",
3030
"pydantic>=2.0.0, <3",
3131
]
3232

src/cleanlab_codex/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# SPDX-License-Identifier: MIT
2-
__version__ = "1.0.7"
2+
__version__ = "1.0.8"

src/cleanlab_codex/codex_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def query(
114114
return self._project.query(
115115
question=question,
116116
fallback_answer=self._fallback_answer,
117-
analytics_metadata=_AnalyticsMetadata(integration_type=IntegrationType.TOOL),
117+
_analytics_metadata=_AnalyticsMetadata(integration_type=IntegrationType.TOOL),
118118
)[0]
119119

120120
def to_openai_tool(self) -> dict[str, Any]:

src/cleanlab_codex/project.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from datetime import datetime
66
from typing import TYPE_CHECKING as _TYPE_CHECKING
7-
from typing import Optional
7+
from typing import Any, Optional
88

99
from codex import AuthenticationError
1010

@@ -178,37 +178,43 @@ def query(
178178
question: str,
179179
*,
180180
fallback_answer: Optional[str] = None,
181-
analytics_metadata: Optional[_AnalyticsMetadata] = None,
181+
metadata: Optional[dict[str, Any]] = None,
182+
_analytics_metadata: Optional[_AnalyticsMetadata] = None,
182183
) -> tuple[Optional[str], Entry]:
183184
"""Query Codex to check if this project contains an answer to the question. If the question is not yet in the project, it will be added for SME review.
184185
185186
Args:
186187
question (str): The question to ask the Codex API.
187188
fallback_answer (str, optional): Optional fallback answer to return if Codex is unable to answer the question.
189+
metadata (dict, optional): Additional custom metadata to associate with the query.
188190
189191
Returns:
190192
tuple[Optional[str], Entry]: A tuple representing the answer for the query and the existing or new entry in the Codex project.
191193
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.
192194
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.
193195
"""
194-
if not analytics_metadata:
195-
analytics_metadata = _AnalyticsMetadata(integration_type=IntegrationType.BACKUP)
196+
if not _analytics_metadata:
197+
_analytics_metadata = _AnalyticsMetadata(integration_type=IntegrationType.BACKUP)
196198

197199
return self._query_project(
198200
question=question,
199201
fallback_answer=fallback_answer,
200-
analytics_metadata=analytics_metadata,
202+
client_metadata=metadata,
203+
analytics_metadata=_analytics_metadata,
201204
)
202205

203206
def _query_project(
204207
self,
205208
question: str,
206209
*,
207210
fallback_answer: Optional[str] = None,
211+
client_metadata: Optional[dict[str, Any]] = None,
208212
analytics_metadata: Optional[_AnalyticsMetadata] = None,
209213
) -> tuple[Optional[str], Entry]:
210214
extra_headers = analytics_metadata.to_headers() if analytics_metadata else None
211-
query_res = self._sdk_client.projects.entries.query(self._id, question=question, extra_headers=extra_headers)
215+
query_res = self._sdk_client.projects.entries.query(
216+
self._id, question=question, client_metadata=client_metadata, extra_headers=extra_headers
217+
)
212218

213219
entry = Entry.model_validate(query_res.entry.model_dump())
214220
if query_res.answer is not None:

tests/test_codex_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_tool_query_passes_metadata(mock_client_from_access_key: MagicMock) -> N
2626
assert kwargs["question"] == "what is the capital of France?"
2727
assert kwargs["fallback_answer"] == CodexTool.DEFAULT_FALLBACK_ANSWER
2828
assert (
29-
kwargs["analytics_metadata"].to_headers()
29+
kwargs["_analytics_metadata"].to_headers()
3030
== _AnalyticsMetadata(integration_type=IntegrationType.TOOL).to_headers()
3131
)
3232

tests/test_project.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,23 @@ def test_query_answer_found(mock_client_from_access_key: MagicMock) -> None:
184184
assert res[1].model_dump() == answered_entry.model_dump()
185185

186186

187+
def test_query_answer_found_with_metadata(mock_client_from_access_key: MagicMock) -> None:
188+
answered_entry = SDKEntry(
189+
id=str(uuid.uuid4()),
190+
question="What is the capital of France?",
191+
answer="Paris",
192+
client_query_metadata=[{"trustworthiness_score": 0.95}],
193+
)
194+
mock_client_from_access_key.projects.entries.query.return_value = EntryQueryResponse(
195+
answer="Paris", entry=answered_entry
196+
)
197+
project = Project(mock_client_from_access_key, FAKE_PROJECT_ID)
198+
res = project.query("What is the capital of France?", metadata={"trustworthiness_score": 0.95})
199+
assert res[0] == answered_entry.answer
200+
assert res[1] is not None
201+
assert res[1].model_dump() == answered_entry.model_dump() # metadata should be included in the entry
202+
203+
187204
def test_add_entries_empty_list(mock_client_from_access_key: MagicMock) -> None:
188205
"""Test adding an empty list of entries"""
189206
project = Project(mock_client_from_access_key, FAKE_PROJECT_ID)

0 commit comments

Comments
 (0)