-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(ph-ai): agent artifacts model #41830
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
Conversation
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.
7 files reviewed, 1 comment
ee/api/agent_artifact.py
Outdated
| def destroy(self, request, *args, **kwargs): | ||
| instance = self.get_object() | ||
|
|
||
| if instance.conversation.user != request.user: | ||
| return Response( | ||
| {"error": "Cannot delete other users' artifacts"}, | ||
| status=status.HTTP_403_FORBIDDEN, | ||
| ) |
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.
style: redundant ownership check - safely_get_queryset already filters by conversation__user=self.request.user, so get_object() will return 404 if user doesn't own the artifact
| def destroy(self, request, *args, **kwargs): | |
| instance = self.get_object() | |
| if instance.conversation.user != request.user: | |
| return Response( | |
| {"error": "Cannot delete other users' artifacts"}, | |
| status=status.HTTP_403_FORBIDDEN, | |
| ) | |
| def destroy(self, request, *args, **kwargs): | |
| return super().destroy(request, *args, **kwargs) |
Prompt To Fix With AI
This is a comment left during a code review.
Path: ee/api/agent_artifact.py
Line: 51:58
Comment:
**style:** redundant ownership check - `safely_get_queryset` already filters by `conversation__user=self.request.user`, so `get_object()` will return 404 if user doesn't own the artifact
```suggestion
def destroy(self, request, *args, **kwargs):
return super().destroy(request, *args, **kwargs)
```
How can I resolve this? If you propose a fix, please make it concise.bb5fddb to
5f3aefb
Compare
049b2f5 to
7b4e4ac
Compare
🔍 Migration Risk AnalysisWe've analyzed your migrations for potential risks. Summary: 1 Safe | 0 Needs Review | 0 Blocked ✅ SafeNo contention risk, backwards compatible Last updated: 2025-11-21 19:37 UTC (53c050f) |
5f3aefb to
f57a488
Compare
7b4e4ac to
e6b941a
Compare
f57a488 to
c22ef9d
Compare
1550750 to
9a0e58b
Compare
c22ef9d to
8eb9a7d
Compare
9a0e58b to
c518e20
Compare
12744ff to
8e15cf1
Compare
2f258be to
b1a4dd3
Compare
8e15cf1 to
2febab6
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.
Left comments mostly about the model. My suggestion is to remove the artifacts endpoint, as there is no use case for now. Also can we merge this PR to master? I'd need it too.
| def save(self, *args, **kwargs): | ||
| max_retries = 5 | ||
| for attempt in range(max_retries): | ||
| try: | ||
| with transaction.atomic(): | ||
| return super().save(*args, **kwargs) | ||
| except IntegrityError as e: | ||
| if "short_id" in str(e) and attempt < max_retries - 1: | ||
| self.short_id = generate_short_id_6() | ||
| else: | ||
| raise |
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.
Should we move it to the logic rather than keep it on the model's level?
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.
Not sure, this will basically always have to be done, anytime we're creating the model.
ee/models/assistant.py
Outdated
| class AgentArtifact(UUIDModel): | ||
| class Type(models.TextChoices): | ||
| VISUALIZATION = "visualization", "Visualization" | ||
| DOCUMENT = "document", "Document" |
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.
It's a bit ambiguous. I assume it's a notebook. But who knows?
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 it can also be a generated (not-saved) insight, it's whatever the agent generates. I need visualizations to be artifacts so they can be reused within deep research, when adding them to the reports, without the agent having to rewrite the queries every single time (it can just reference the short_id)
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.
So what is the document? Is it just some generic text?
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.
It's a notebook-like document, but TipTap-agnostic, I have the model in the next PR.
It can be something like:
- markdown block
- visualization
- markdown
- session replay video
This can be easily extended with runnable code blocks or whatever, I'm thinking about a Jupyter notebook final use case, which also works right now for research/session summaries. This is basically a document that we can more easily display in the frontend, without passing by the full Prosemirror text editor, which is super slow.
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.
I'll rephrase the question: For this enum type, is there only a single Pydantic schema, or are there multiple?
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.
Aaaah ok, I thought you were saying that AgentArtifact is ambiguous, not the Document enum.
There would be only one Pydantic schema (the document), with sub-blocks for the different content elements.
I can rename it to notebook, just didn't want to collide with the existing Notebook model.
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.
Got it, cool. Let's maybe rename it to the notebook? It doesn't collide since it's a member of an enum type. Just want to make sure the model is easy to understand.
ee/api/agent_artifact.py
Outdated
| def destroy(self, request, *args, **kwargs): | ||
| instance = self.get_object() | ||
|
|
||
| if instance.conversation.user != request.user: |
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 always scope by tenant, so I think it's fine to have deletion at the tenant level. This goes a bit to RBAC.
2febab6 to
81c7220
Compare
23c326b to
b465263
Compare
396ac44 to
7363a7c
Compare
91a286e to
0f84b95
Compare
d1cd8ab to
f253c04
Compare
402dc8b to
fc07aea
Compare
f253c04 to
5a1c5f3
Compare
fc07aea to
84da926
Compare
5a1c5f3 to
2b377bf
Compare
514a053 to
e40b7da
Compare
70bf6e7 to
8f58081
Compare
e096ffb to
3120090
Compare
8f58081 to
ccc4fe0
Compare
3120090 to
2101098
Compare
7113cf6 to
0fc5130
Compare
2101098 to
3f2b627
Compare
0fc5130 to
458276d
Compare
3f2b627 to
53c050f
Compare
458276d to
55edaab
Compare

Problem
Conversation that started this here
We need a way to store and manage artifacts created by the AI agents, such as visualizations and documents.
Changes
AgentArtifactmodel to store artifacts generated during conversationsImplemented a REST API for retrieving, listing, and deleting artifactsHow did you test this code?
Added tests