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

[Obs AI Assistant] Share conversations #211854

Open
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

viduni94
Copy link
Contributor

@viduni94 viduni94 commented Feb 19, 2025

Closes #206590
Closes #211710
Closes #211604
Closes https://github.com/elastic/obs-ai-assistant-team/issues/215

Summary

This PR implements conversation sharing for Obs AI Assistant conversations.

The features included are as follows:

  1. Refactored ChatActionsMenu - Removed Copy Conversation and Duplicate options
  2. Removed the banner added in [Obs AI Assistant] Duplicate conversations #209382
  3. Removed the conversation input box (PromptEditor), if the user who is viewing the conversation cannot continue it.
  4. Implemented a ChatBanner - This will show whether a conversation is shared with the team (The banner content differs based on who is viewing the conversation)
  5. Implemented ChatContextMenu for conversation specific actions. This includes "Duplicate", "Copy conversation", "Copy URL" and "Delete". "Delete" functionality is only available to the conversation owner. (This menu is only included in the ChatHeader at the moment because Eui doesn't support passing a node to EuiListGroupItem to include this in the ConversationList. This will be refactored in [Obs AI Assistant] Archiving conversations #209386)
  6. Implemented useConversationContextMenu for "copy" and "delete" functionalities.
  7. Implemented ChatSharingMenu to mark a conversation as shared/private. This is only enabled for the owner of the conversation. For other users, a disabled badge will be shown stating whether the conversation is Private or Shared.
  8. Implemented updateConversationAccess route.
  9. Updated the Chat Item Actions Inspect Prompt Button to Inspect. This was eye before.
  10. Implemented a custom component ConversationListItemLabel to show the shared icon in ConversationList.
  11. Re-named "Copy conversation" to "Copy to clipboard" to avoid ambiguity with "Duplicate".
  12. Added success toast on "Copy to clipboard"

Note: If a conversation started from contextual insights, and then the user continue the conversation --> The conversation will be stored. However, if the user deletes the continued conversation, they will be reverted to the initial messages from the contextual insights.

Screen recording

sharing-conversations.mov

Checklist

@viduni94 viduni94 added release_note:feature Makes this part of the condensed release notes Team:Obs AI Assistant Observability AI Assistant backport:version Backport to applied version labels v9.1.0 v8.19.0 labels Feb 19, 2025
@viduni94 viduni94 self-assigned this Feb 19, 2025
@viduni94 viduni94 changed the title [Obs AI Assistant] Sharing conversations [Obs AI Assistant] Share conversations Feb 19, 2025
@viduni94 viduni94 force-pushed the sharing-conversations branch 5 times, most recently from 5e7a5ff to b487cf0 Compare February 26, 2025 14:23
@viduni94 viduni94 force-pushed the sharing-conversations branch from f467bed to e6bdd08 Compare February 27, 2025 16:30
@viduni94 viduni94 added the ci:project-deploy-observability Create an Observability project label Mar 5, 2025
Copy link
Contributor

github-actions bot commented Mar 5, 2025

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

@kibanamachine
Copy link
Contributor

Project deployed, see credentials at: https://buildkite.com/elastic/kibana-deploy-project-from-pr/builds/354

@@ -408,7 +379,6 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte
'And what are SLIs?'
);
await testSubjects.pressEnter(ui.pages.conversations.chatInput);
log.info('SQREN: Waiting for the message to be displayed');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sorenlouv Let me know if these were left intentionally.. I can add it back

@viduni94 viduni94 marked this pull request as ready for review March 5, 2025 23:50
@viduni94 viduni94 requested review from a team as code owners March 5, 2025 23:50
@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ai-assistant (Team:Obs AI Assistant)

@elasticmachine
Copy link
Contributor

elasticmachine commented Mar 6, 2025

💔 Build Failed

  • Buildkite Build
  • Commit: 69a0870
  • Kibana Serverless Image: docker.elastic.co/kibana-ci/kibana-serverless:pr-211854-69a0870d65a2

Failed CI Steps

Test Failures

  • [job] [logs] Jest Tests #4 / ConversationList calls delete conversation when delete icon is clicked
  • [job] [logs] Jest Tests #4 / ConversationList calls delete conversation when delete icon is clicked
  • [job] [logs] Jest Tests #4 / ConversationList calls onConversationSelect when a conversation is clicked
  • [job] [logs] Jest Tests #4 / ConversationList calls onConversationSelect when a conversation is clicked
  • [job] [logs] Jest Tests #4 / ConversationList displays error state
  • [job] [logs] Jest Tests #4 / ConversationList displays error state
  • [job] [logs] Jest Tests #4 / ConversationList displays loading state
  • [job] [logs] Jest Tests #4 / ConversationList displays loading state
  • [job] [logs] Jest Tests #4 / ConversationList renders "no conversations" message when there are no conversations
  • [job] [logs] Jest Tests #4 / ConversationList renders "no conversations" message when there are no conversations
  • [job] [logs] Jest Tests #4 / ConversationList renders a new chat button and triggers onConversationSelect when clicked
  • [job] [logs] Jest Tests #4 / ConversationList renders a new chat button and triggers onConversationSelect when clicked
  • [job] [logs] Jest Tests #4 / ConversationList renders categorized conversations
  • [job] [logs] Jest Tests #4 / ConversationList renders categorized conversations
  • [job] [logs] Jest Tests #4 / ConversationList renders the component without errors
  • [job] [logs] Jest Tests #4 / ConversationList renders the component without errors

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
observabilityAIAssistantApp 428 434 +6
observabilityAiAssistantManagement 376 382 +6
searchAssistant 257 263 +6
total +18

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/ai-assistant 78 102 +24
observabilityAIAssistant 385 391 +6
total +30

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
observabilityAIAssistantApp 255.1KB 264.2KB +9.1KB
searchAssistant 149.7KB 158.2KB +8.6KB
total +17.7KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
observabilityAIAssistant 38.0KB 38.1KB +115.0B
searchAssistant 5.4KB 5.4KB -3.0B
total +112.0B
Unknown metric groups

API count

id before after diff
@kbn/ai-assistant 78 102 +24
observabilityAIAssistant 387 393 +6
total +30

History

cc @viduni94

@@ -625,5 +625,156 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
});
});
});

describe('conversation sharing', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've verified these in stateful and serverless local. Will be testing on MKI tomorrow.

@@ -351,6 +346,65 @@ export function ChatBody({
}
};

const handleConversationAccessUpdate = async (access: ConversationAccess) => {
await updateConversationAccess(access);
conversation.refresh();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need an additional call to refresh the conversation after changing access?
The UI is already updated, but should we refresh the conversation if there’s an error when calling updateConversationAccess(access)

/>
);
}
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

These two cases are functionally identical:

  • "Public, conversation has a user, but current user is not the owner. Don't show prompt editor, only show the banner with Duplicate button"
  • "Public, but conversation doesn't have a user (for backwards compatibility with old conversations generated by the rule connector). Don't show prompt editor, only show the banner with Duplicate button"

Since isConversationOwnedByCurrentUser already checks if the conversation has no user (returning false in both cases), the UI behavior remains the same (showPromptEditor and sharedBanner).
You can delete one of them, avoiding duplicated code.

@@ -216,12 +216,46 @@ const deleteConversationRoute = createObservabilityAIAssistantServerRoute({
},
});

const updateConversationAccessRoute = createObservabilityAIAssistantServerRoute({
Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think we should add a separate router for each conversation property update. We already have an update router that we can use, and we can also remove the dedicated update Title router.

We have two possible approaches:

  • Only keep PUT conversation router. Use PUT to update the entire conversation. The request must contain the full updated conversation document.
  • Remove title and access routers, keep PUT conversation, add PATCH conversation. Use PATCH to update specific fields, The request includes only the properties being modified (e.g., title and access).

what do you think?

access: ConversationAccess;
}) => {
if (![ConversationAccess.SHARED, ConversationAccess.PRIVATE].includes(access)) {
throw badRequest('Invalid access value. Allowed values are SHARED and PRIVATE.');
Copy link
Contributor

Choose a reason for hiding this comment

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

This validation/error seems more appropriate at the router level rather than in the client method.

}

const conversation = await this.get(conversationId);
if (!conversation) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use either getConversationWithMetaFields or get, as both methods query the document. The get method internally calls getConversationWithMetaFields and also validates if the document exists.

conversationId: t.string,
}),
body: t.type({
access: t.string,
Copy link
Member

Choose a reason for hiding this comment

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

enum? (t.literal)

access: params.body.access as ConversationAccess,
});

return Promise.resolve(conversation);
Copy link
Member

Choose a reason for hiding this comment

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

why do we need Promise.resolve here?

@@ -140,7 +141,7 @@ export class ObservabilityAIAssistantClient {
return false;
}

return conversation.user.id
return conversation.user.id && user.id
Copy link
Member

Choose a reason for hiding this comment

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

In what case is user.id not set, but does the current user still have access to the conversation?

Comment on lines +666 to +678
const updatedConversation: Conversation = merge({}, conversation, {
public: isPublic,
conversation: {
last_updated: new Date().toISOString(),
},
});

await this.dependencies.esClient.asInternalUser.update({
id: document._id!,
index: document._index,
doc: updatedConversation,
refresh: true,
});
Copy link
Member

Choose a reason for hiding this comment

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

can we use the .update function on the AI Assistant client instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:version Backport to applied version labels ci:project-deploy-observability Create an Observability project release_note:feature Makes this part of the condensed release notes Team:Obs AI Assistant Observability AI Assistant v8.19.0 v9.1.0
Projects
None yet
5 participants