-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Attempt to detect content category (#22)
* fix bug in render_category_selection * build the ui workflow to automatically detect content category * allow overloading loading state in chat_item * write the logic and endpoint to detect content category * add improved workflow for selecting auto-detected contentcategory * move ui logic for auto_detected_category to own file * add createdAt property to chat items and implement error handling for loading state * allow user to regenerate errored ai responses
- Loading branch information
1 parent
c68ed25
commit 350081a
Showing
9 changed files
with
258 additions
and
21 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from pydantic import BaseModel | ||
|
||
from src.services.gemini_client import get_gemini | ||
from src.utils.chat_utils import ContentCategory, content_categories | ||
|
||
|
||
class DetectContentCategoryRequest(BaseModel): | ||
content: str | ||
|
||
|
||
def detect_category_prompt(content: str) -> str: | ||
""" | ||
System Prompt to detect the category of a given content | ||
""" | ||
return f"""You are an intelligent content type classifier. Your task is to analyze the given content and categorize it into one of the following types: | ||
CONTENT: "{content}" | ||
CATEGORIES: {', '.join(content_categories)} | ||
IMPORTANT NOTE: | ||
- You must ONLY output one of these exact category names, with no additional text, explanation, preamble or formatting. | ||
- If the content doesn't fit any of the categories, you should output "other". | ||
Examples: | ||
Input: "Welcome to today's episode where we'll be discussing the fascinating world of quantum computing..." | ||
Output: podcast | ||
Input: "And now, dear brothers and sisters, let us reflect on the profound message in today's scripture..." | ||
Output: sermon | ||
Input: "Let's dive into today's lecture on advanced machine learning algorithms..." | ||
Output: lecture | ||
""" | ||
|
||
|
||
def validate_category_output(output: str) -> ContentCategory: | ||
""" | ||
Validate that the AI output is a valid content category. | ||
Throws ValueError if the output is not a valid category. | ||
""" | ||
cleaned_output = output.strip().lower() | ||
if cleaned_output not in content_categories: | ||
raise ValueError(f"Invalid category '{cleaned_output}'. Must be one of: {', '.join(content_categories)}") | ||
return cleaned_output | ||
|
||
|
||
async def detect_content_category(content: str) -> ContentCategory: | ||
""" | ||
Detect the category of the given content using Gemini Flash. | ||
""" | ||
client = get_gemini() | ||
|
||
model = client.GenerativeModel( | ||
model_name="gemini-1.5-flash-002", | ||
system_instruction=detect_category_prompt(content), | ||
generation_config=client.GenerationConfig( | ||
temperature=0.1, | ||
max_output_tokens=30, | ||
response_mime_type="text/plain", | ||
), | ||
) | ||
|
||
response = model.generate_content(["Now, please categorize the content."]) | ||
if not response.text: | ||
raise Exception("Error obtaining response from Gemini Flash") | ||
|
||
return validate_category_output(response.text) |
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,43 @@ | ||
<script lang="ts" context="module"> | ||
import type { ContentCategory } from '@/utils/types'; | ||
export const categoryArticleMap: Record<ContentCategory, string> = { | ||
podcast: 'a podcast', | ||
sermon: 'a sermon', | ||
audiodrama: 'an audiodrama', | ||
lecture: 'a lecture', | ||
commentary: 'a commentary', | ||
voicenote: 'a voicenote', | ||
interview: 'an interview', | ||
soundbite: 'a soundbite' | ||
}; | ||
</script> | ||
|
||
<script lang="ts"> | ||
import ChatListItem from './ChatListItem.svelte'; | ||
import { Button } from './ui/button'; | ||
import { createEventDispatcher } from 'svelte'; | ||
import { ArrowRight } from 'lucide-svelte'; | ||
export let category: ContentCategory; | ||
const dispatch = createEventDispatcher<{ selectCategory: { value: ContentCategory } }>(); | ||
$: categoryWithArticle = `"${categoryArticleMap[category]}"`; | ||
</script> | ||
|
||
<ChatListItem | ||
type="assistant" | ||
content="I auto-detected you want {categoryWithArticle}. Press NEXT to continue if correct." | ||
/> | ||
|
||
<Button | ||
variant="ghost" | ||
class="text-base px-10 py-6 bg-gray-800 w-fit hover:bg-gray-700" | ||
on:click={() => dispatch('selectCategory', { value: category })} | ||
> | ||
<span> Next </span> | ||
<ArrowRight class="w-4 ml-1 inline" /> | ||
</Button> | ||
|
||
<ChatListItem type="assistant" content="Else, select your audiocast category" /> |
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 |
---|---|---|
@@ -1,13 +1,56 @@ | ||
<script lang="ts"> | ||
import ChatListItem from './ChatListItem.svelte'; | ||
import SelectCategory from './SelectCategory.svelte'; | ||
import type { ContentCategory } from '@/utils/types'; | ||
import SelectCategory, { categories } from './SelectCategory.svelte'; | ||
import { env } from '@env'; | ||
import AutoDetectedCategory from './AutoDetectedCategory.svelte'; | ||
export let content: string; | ||
let detectingCategory = false; | ||
let detectedCategory: ContentCategory | null = null; | ||
$: getCategorySelection(); | ||
async function getCategorySelection() { | ||
if (detectingCategory) return; | ||
detectingCategory = true; | ||
return fetch(`${env.API_BASE_URL}/detect-category`, { | ||
method: 'POST', | ||
body: JSON.stringify({ content }), | ||
headers: { 'Content-Type': 'application/json' } | ||
}) | ||
.then<ContentCategory>((res) => { | ||
if (res.ok) return res.json(); | ||
throw new Error(res.statusText); | ||
}) | ||
.then((res) => categories.includes(res) && (detectedCategory = res)) | ||
.catch(() => void 0) | ||
.finally(() => (detectingCategory = false)); | ||
} | ||
</script> | ||
|
||
<!-- Rename to get_category_selection.svelte --> | ||
|
||
<div class="flex flex-col gap-y-3 h-full"> | ||
<ChatListItem type="user" {content} /> | ||
<div class="flex flex-col gap-y-3 w-full"> | ||
<ChatListItem type="user" {content} /> | ||
|
||
<ChatListItem type="assistant" content="Please select your audiocast category" /> | ||
{#if detectingCategory} | ||
<ChatListItem type="assistant" content="Auto-detecting content category..." loading> | ||
<span slot="loading" class="animate-pulse"> | ||
Auto-detecting content category...Please wait | ||
</span> | ||
</ChatListItem> | ||
{:else if detectedCategory} | ||
<AutoDetectedCategory category={detectedCategory} on:selectCategory /> | ||
{:else} | ||
<ChatListItem type="assistant" content="Please select your audiocast category" /> | ||
{/if} | ||
</div> | ||
|
||
<SelectCategory on:selectCategory /> | ||
{#if !detectingCategory} | ||
<SelectCategory on:selectCategory /> | ||
{/if} | ||
</div> |
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.