-
Notifications
You must be signed in to change notification settings - Fork 471
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Proactive Notifications Apps (#1240)
## Overview Issue: #1235 #1132 ## Features - Developer could create new apps with the proactive notification capacity ## Usages - Developer create a new app with capacity `proactive_notification`, and `external_integration` to trigger the `transcript_processed`. ``` { "id": "mentor-01", ... "capabilities": [ "external_integration", "proactive_notification", ], "external_integration": { "triggers_on": "transcript_processed", "webhook_url": "https://based-hardware-development--plugins-api.modal.run/mentor", ... }, "proactive_notification": { "scopes": ["user_name", "user_facts"] }, ... } ``` - Webhook respond with the format: ``` { "mentor": { "prompt": "the prompt template, with `{{user_name}}` and `{{user_facts}}`, Omi will use this prompt to ask LLM then send a notification to user", "params": ["user_name", "user_facts"] } } ``` - (Optional) you could detect the codeword before responding with the `mentor` instruction, either by using the LLM or just a simple regex: ``` ai_names = ['Omi', 'Omie', 'Homi', 'Homie'] codewords = [f'hey {ai_name} what do you think' for ai_name in ai_names] ai_name in ai_names] transcript = TranscriptSegment.segments_as_string(segments) text_lower = normalize(transcript) pattern = r'\b(?:' + '|'.join(map(re.escape, [normalize(cw) for cw in codewords])) + r')\b' if bool(re.search(pattern, text_lower)): # respond ``` ## Technical details <img width="1074" alt="Screenshot 2024-11-06 at 10 43 46" src="https://github.com/user-attachments/assets/09a18457-6116-454b-98a8-d36dd9a7b1d2"> ## Examples ![382703065-af9b26f9-ddc5-4909-974c-c83ae4dc658e](https://github.com/user-attachments/assets/131cedfe-7588-4770-aa5e-9cea9a0e2d67) ## TODO - [x] A dead simple app which could trigger codeword and send the notification - [x] Put the LLM to it - [x] `user_facts` scope - [x] Put capacity `proactive_notification` - [x] Refine the document ## Future idea - `memory_context` scope - rate limits per plugin per user per 5s - 1 ## 🚀 Deploy Steps - [ ] Merge https://github.com/BasedHardware/omi/pull/1240/files - [ ] Create plugin. ``` curl -X 'POST' \ 'https://based-hardware-development--backend-thinh-v2-api.modal.run/v3/plugins' \ -H 'accept: application/json' \ -H 'authorization: <KEY>' \ -H 'Content-Type: multipart/form-data' \ -F 'plugin_data={"name":"Mentor.01","author":"@thinh","description":"Mentor.01 - An AI-powered mentor, designed to elevate your meetings and help you achieve your goals. With its insightful guidance and real-time support, you'\''ll gain the confidence and skills to excel in every interaction.","image":"/plugins/logos/mentor_01.jpg","capabilities":["external_integration","proactive_notification"],"external_integration":{"triggers_on":"transcript_processed","webhook_url":"https://based-hardware-development--plugins-api.modal.run/mentor","setup_completed_url":"https://based-hardware-development--plugins-api.modal.run/setup/mentor","setup_instructions_file_path":"https://raw.githubusercontent.com/BasedHardware/Omi/main/plugins/instructions/mentor_01/README.md"},"proactive_notification":{"scopes":["user_name","user_facts"]},"deleted":false,"private":false}' \ -F 'file=@mentor_01.jpg;type=image/jpeg' ``` - [ ] Deploy Pusher service - [ ] Deploy Plugin service
- Loading branch information
Showing
14 changed files
with
221 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import re | ||
import time | ||
|
||
from fastapi import APIRouter | ||
|
||
from models import * | ||
from db import get_upsert_segment_to_transcript_plugin | ||
|
||
router = APIRouter() | ||
|
||
scan_segment_session = {} | ||
|
||
# ******************************************************* | ||
# ************ Basic Mentor Plugin ************ | ||
# ******************************************************* | ||
|
||
@router.post('/mentor', tags=['mentor', 'basic', 'realtime'], response_model=MentorEndpointResponse) | ||
def mentoring(data: RealtimePluginRequest): | ||
def normalize(text): | ||
return re.sub(r' +', ' ',re.sub(r'[,?.!]', ' ', text)).lower().strip() | ||
|
||
# Add segments by session_id | ||
session_id = data.session_id | ||
segments = get_upsert_segment_to_transcript_plugin('mentor-01', session_id, data.segments) | ||
scan_segment = scan_segment_session[session_id] if session_id in scan_segment_session and len(segments) > len(data.segments) else 0 | ||
|
||
# Detect codewords | ||
ai_names = ['Omi', 'Omie', 'Homi', 'Homie'] | ||
codewords = [f'hey {ai_name} what do you think' for ai_name in ai_names] | ||
scan_segments = TranscriptSegment.combine_segments([], segments[scan_segment:]) | ||
if len(scan_segments) == 0: | ||
return {} | ||
text_lower = normalize(scan_segments[-1].text) | ||
pattern = r'\b(?:' + '|'.join(map(re.escape, [normalize(cw) for cw in codewords])) + r')\b' | ||
if not bool(re.search(pattern, text_lower)): | ||
return {} | ||
|
||
# Generate mentoring prompt | ||
scan_segment_session[session_id] = len(segments) | ||
transcript = TranscriptSegment.segments_as_string(segments) | ||
|
||
user_name = "{{user_name}}" | ||
user_facts = "{{user_facts}}" | ||
|
||
prompt = f""" | ||
You are an experienced mentor, that helps people achieve their goals during the meeting. | ||
You are advising {user_name} right now. | ||
{user_facts} | ||
The following is a {user_name}'s conversation, with the transcripts, that {user_name} had during the meeting. | ||
{user_name} wants to get the call-to-action advice to move faster during the meetting based on the conversation. | ||
First, identify the topics or problems that {user_name} is discussing or trying to resolve during the meeting, and then provide advice specific to those topics or problems. If you cannot find the topic or problem of the meeting, respond with an empty message. | ||
The advice must focus on the specific object mentioned in the conversation. The object could be a product, a person, or an event. | ||
The response must follow this format: | ||
Noticed you are trying to <meeting topics or problems>. | ||
If I were you, I'd <actions>. | ||
Remember {user_name} is busy so this has to be very efficient and concise. | ||
Respond in at most 100 words. | ||
Output your response in plain text, without markdown. | ||
``` | ||
${transcript} | ||
``` | ||
""".replace(' ', '').strip() | ||
|
||
return {'session_id': data.session_id, | ||
'mentor': {'prompt': prompt, | ||
'params': ['user_name', 'user_facts']}} | ||
|
||
@ router.get('/setup/mentor', tags=['mentor']) | ||
def is_setup_completed(uid: str): | ||
return {'is_setup_completed': True} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.