-
Notifications
You must be signed in to change notification settings - Fork 700
OPIK-1764: Add feedback scores endpoint #2506
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
OPIK-1764: Add feedback scores endpoint #2506
Conversation
…hiagohora/OPIK-1765_make_threads_first_class_citizens
… into thiagohora/OPIK-1765_implement_closing_thread_mechanism
… into thiagohora/OPIK-1765_implement_closing_thread_mechanism
Backend Tests Results 177 files 177 suites 17m 56s ⏱️ Results for commit 0fae314. ♻️ This comment has been updated with latest results. |
803ac15
to
5eaf057
Compare
SDK E2E Tests Results0 tests 0 ✅ 0s ⏱️ Results for commit 27a002b. |
SDK E2E Tests Results0 tests 0 ✅ 0s ⏱️ Results for commit 27a002b. ♻️ This comment has been updated with latest results. |
27a002b
to
9ffb541
Compare
.../opik-backend/src/main/java/com/comet/opik/api/resources/v1/jobs/TraceThreadsClosingJob.java
Outdated
Show resolved
Hide resolved
var workspaceId = requestContext.get().getWorkspaceId(); | ||
String projectName = scores.projectName(); | ||
|
||
Optional<Project> project = projectService.findByNames(workspaceId, List.of(projectName)).stream().findFirst(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to find a way to hide the logic in the service (here we have another point where logic been done on the resources level) - any suggestions? - maybe adding a facade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have cases where we use a facade, for instance, the experiment build API, which handles many domain services. We need to be careful when adding services as dependencies of other services. As a rule, it's not critical, but this frequently leads to cycle dependencies.< 10000 /p>
In this case, I will change, but before implementing such a practice, I would open a discussion with the team. In cases like this, it doesn't add much value to introducing facades. Usually, facade
coordinates complex operations across different domain operations, simplifies the APIs, and reduces the complexity of dealing with such operations. In this case, we are referring to a simple 'fail fast' exit using an already existing dependency of the controller. Again, no big deal changing it, but it's been suggested before starting to implement it in all system.
@Valid public record FeedbackScoreBatch( | ||
@NotNull @Size(min = 1, max = 1000) List<@Valid FeedbackScoreBatchItem> scores) implements RateEventContainer { | ||
|
||
public static class View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you share more info about this hierarchy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a JSON view, which is used extensively throughout the system. It allows us to use the same Pojo, but with different JSON representations of it. In this case, it will enable us to use the same FeedbackScoreBatchItem
POJO used across the system, but adding the `thread_id' to it. This is only required here; for all other endpoints, this property is not exposed to the client.
apps/opik-backend/src/main/java/com/comet/opik/domain/threads/TraceThreadIdService.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@Override | ||
public Mono<UUID> getThreadModelId(@NonNull UUID projectId, @NonNull String threadId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what dose the "Model" stands for? (method name)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The model is a Java Pojo that represents the persistence view of the domain object. In this case, the threadId
already represents the external ID used by the client, and the threadModelId
represents the internal Opik ID, which identifies the thread from the Opik side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added comments
apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java
Show resolved
Hide resolved
apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java
Show resolved
Hide resolved
apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java
Show resolved
Hide resolved
.map(threadIdMap -> bindThreadModelId(projectDto, threadIdMap)) | ||
.filter(projectDtoWithThreads -> !projectDtoWithThreads.scores().isEmpty()) | ||
// score the batch of threads with resolved thread model IDs | ||
.flatMap(score -> dao.scoreBatchOfThreads(score.scores())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plz rename the dao to spesifi which dao is been used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I would recommend that we discuss this practice with the team, since we likely have different opinions about it, and it is currently being used in other parts of the system. This is the FeedbackScoresService
, so contextually, I believe it is simple to infer that the DAO it relates to is FeedbackScores, unless the name specifies otherwise.
.map(threadModelId -> Map.entry(threadId, threadModelId)); | ||
} | ||
|
||
private ProjectDto bindThreadModelId(ProjectDto projectDto, Map<String, UUID> threadIdMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dose this method is generating projectDTO with score and threadID? can we have more self explanatory name for this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, if we check the implementation, we only copy the object and add the threadModelId
to it since it's an immutable object.
...es/liquibase/db-app-analytics/migrations/000029_add_thread_enum_value_to_feedback_scores.sql
Show resolved
Hide resolved
...k-backend/src/test/java/com/comet/opik/api/resources/v1/jobs/TraceThreadsClosingJobTest.java
Outdated
Show resolved
Hide resolved
da5dafa
to
0a748a8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Enhances feedback scoring by introducing thread-level scoring and deletion endpoints, updates the domain and data layers to handle thread entities, and augments the API and client models.
- Add “thread” entity type in
EntityType
, Liquibase migration, DAO, and service layers - Implement new REST endpoints in
TracesResource
for batch scoring and deleting thread feedback - Extend client utilities, API models (
FeedbackScoreBatch
,FeedbackScoreBatchItem
,DeleteThreadFeedbackScores
), and tests for thread support
Reviewed Changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
TraceResourceClient.java |
Added test client methods for thread feedback endpoints |
000029_add_thread_enum_value_to_feedback_scores.sql |
Liquibase migration: add thread to feedback_scores ENUM |
TraceThreadService.java |
New interface methods for retrieving/creating thread IDs |
TraceThreadIdService.java |
Implemented getThreadModelId with caching and error handling |
FeedbackScoreService.java |
Added batch scoring & deletion logic for thread feedback |
FeedbackScoreDAO.java |
DAO methods for thread score batch and deletion by name |
EntityType.java |
Introduced THREAD enum value |
TracesResource.java |
Exposed PUT/POST endpoints for thread feedback scoring/deletion |
SpansResource.java |
Updated @JsonView on span batch scoring endpoint |
FeedbackScoreBatchItem.java |
Extended record with threadId and view-based validation |
FeedbackScoreBatch.java |
Added validation groups for tracing vs. thread views |
DeleteThreadFeedbackScores.java |
New request record for deleting thread feedback scores |
DeleteFeedbackScore.java |
Removed validation on name field |
Comments suppressed due to low confidence (3)
apps/opik-backend/src/main/java/com/comet/opik/api/DeleteFeedbackScore.java:11
- Re-add a validation constraint like
@NotBlank
or@NotNull
on thename
field to prevent invalid delete requests.
public record DeleteFeedbackScore(String name) {
apps/opik-backend/src/main/java/com/comet/opik/domain/threads/TraceThreadIdService.java:58
- Add the missing imports for
com.google.common.base.Preconditions
andorg.apache.commons.lang3.StringUtils
to avoid compilation errors.
Preconditions.checkArgument(!StringUtils.isBlank(workspaceId), "Workspace ID cannot be blank");
apps/opik-backend/src/main/java/com/comet/opik/domain/threads/TraceThreadIdService.java:59
- Ensure that
Preconditions
andStringUtils
are imported so these argument checks compile correctly.
Preconditions.checkArgument(!StringUtils.isBlank(threadId), "Thread ID cannot be blank");
0a748a8
to
0fae314
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, a few minor comments
@@ -689,4 +691,50 @@ public Response closeTraceThread( | |||
return Response.noContent().build(); | |||
} | |||
|
|||
@PUT | |||
@Path("/threads/feedback-scores") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we want to create a new Resource class for Threads instead of adding them to Traces?
@@ -171,4 +183,112 @@ public Mono<FeedbackScoreNames> getProjectsFeedbackScoreNames(Set<UUID> projectI | |||
.map(names -> names.stream().map(FeedbackScoreNames.ScoreName::new).toList()) | |||
.map(FeedbackScoreNames::new); | |||
} | |||
|
|||
@Override | |||
public Mono<Void> scoreBatchOfThreads(List<FeedbackScoreBatchItem> scores) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public Mono<Void> scoreBatchOfThreads(List<FeedbackScoreBatchItem> scores) { | |
public Mono<Void> scoreBatchOfThreads(@NonNull List<FeedbackScoreBatchItem> scores) { |
String score1 = RandomStringUtils.secure().nextAlphanumeric(10); | ||
String score2 = RandomStringUtils.secure().nextAlphanumeric(10); | ||
String score3 = RandomStringUtils.secure().nextAlphanumeric(10); | ||
|
||
scoreItems.set(0, scoreItems.get(0).toBuilder() | ||
.threadId(threadId) | ||
.projectName(projectName) | ||
.projectId(null) | ||
.name(score1) | ||
.build()); | ||
|
||
scoreItems.set(1, scoreItems.get(1).toBuilder() | ||
.threadId(threadId) | ||
.projectName(projectName) | ||
.projectId(null) | ||
.name(score2) | ||
.build()); | ||
|
||
scoreItems.set(2, scoreItems.get(2).toBuilder() | ||
.threadId(threadId) | ||
.projectName(projectName) | ||
.projectId(null) | ||
.name(score3) | ||
.build()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use some for loop or stream for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one doesn't seem worth it since we are changing the object generated by PodamFactoryUtils.manufacturePojoList
Thanks! I will address those comments in the following PR |
Details