Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop/src/chat/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class CustomChatTransport implements ChatTransport<HyprUIMessage> {
constructor(
private registry: ToolRegistry,
private model: LanguageModel,
private systemPrompt?: string,
) {}

sendMessages: ChatTransport<HyprUIMessage>["sendMessages"] = async (
Expand All @@ -22,6 +23,7 @@ export class CustomChatTransport implements ChatTransport<HyprUIMessage> {

const agent = new Agent({
model: this.model,
system: this.systemPrompt,
tools,
stopWhen: stepCountIs(5),
prepareStep: async ({ messages }) => {
Expand Down
20 changes: 17 additions & 3 deletions apps/desktop/src/components/chat/session.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useChat } from "@ai-sdk/react";
import type { ChatStatus } from "ai";
import { type ReactNode, useEffect, useMemo, useRef } from "react";
import { type ReactNode, useEffect, useMemo, useRef, useState } from "react";

import { commands as templateCommands } from "@hypr/plugin-template";
import type { ChatMessage, ChatMessageStorage } from "@hypr/store";

import { CustomChatTransport } from "../../chat/transport";
Expand Down Expand Up @@ -163,14 +164,27 @@ export function ChatSession({
function useTransport() {
const registry = useToolRegistry();
const model = useLanguageModel();
const language = main.UI.useValue("ai_language", main.STORE_ID) ?? "en";
const [systemPrompt, setSystemPrompt] = useState<string | undefined>();

useEffect(() => {
templateCommands
.render("chat.system", { language })
.then((result) => {
if (result.status === "ok") {
setSystemPrompt(result.data);
}
})
.catch(console.error);
}, [language]);

const transport = useMemo(() => {
if (!model) {
return null;
}

return new CustomChatTransport(registry, model);
}, [registry, model]);
return new CustomChatTransport(registry, model, systemPrompt);
}, [registry, model, systemPrompt]);

return transport;
}
14 changes: 11 additions & 3 deletions apps/desktop/src/components/settings/general/main-language.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {

type ISO_639_1_CODE = keyof typeof LANGUAGES_ISO_639_1;

export function MainLanguageView(_: {
export function MainLanguageView({
value,
onChange,
supportedLanguages,
}: {
value: string;
onChange: (value: string) => void;
supportedLanguages: ISO_639_1_CODE[];
Expand All @@ -23,12 +27,16 @@ export function MainLanguageView(_: {
Language for summaries, chats, and AI-generated responses
</p>
</div>
<Select value="English" disabled>
<Select value={value} onValueChange={onChange}>
<SelectTrigger className="w-40 shadow-none focus:ring-0 focus:ring-offset-0">
<SelectValue />
</SelectTrigger>
<SelectContent className="max-h-[250px] overflow-auto">
<SelectItem value="English">English</SelectItem>
{supportedLanguages.map((code) => (
<SelectItem key={code} value={code}>
{LANGUAGES_ISO_639_1[code].name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,25 @@ async function transformArgs(

const sessionContext = getSessionContext(sessionId, store);
const template = templateId ? getTemplateData(templateId, store) : undefined;
const language = getLanguage(store);

return {
sessionId,
enhancedNoteId,
rawMd: sessionContext.rawMd,
language,
sessionData: sessionContext.sessionData,
participants: sessionContext.participants,
segments: sessionContext.segments,
template,
};
}

function getLanguage(store: MainStore): string {
const value = store.getValue("ai_language");
return typeof value === "string" && value.length > 0 ? value : "en";
}

function getSessionContext(sessionId: string, store: MainStore) {
return {
rawMd: getStringCell(store, "sessions", sessionId, "raw_md"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ async function* executeWorkflow(params: {
async function getSystemPrompt(args: TaskArgsMapTransformed["enhance"]) {
const result = await templateCommands.render("enhance.system", {
hasTemplate: !!args.template,
language: args.language,
});

if (result.status === "error") {
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/store/zustand/ai-task/task-configs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface TaskArgsMapTransformed {
sessionId: string;
enhancedNoteId: string;
rawMd: string;
language: string;
sessionData: {
title: string;
started_at?: string;
Expand Down Expand Up @@ -50,6 +51,7 @@ export interface TaskArgsMapTransformed {
title: {
sessionId: string;
enhancedMd: string;
language: string;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ async function transformArgs(
store: MainStore,
): Promise<TaskArgsMapTransformed["title"]> {
const enhancedMd = readEnhancedMarkdown(store, args.sessionId);
return { ...args, enhancedMd };
const language = getLanguage(store);
return { ...args, enhancedMd, language };
}

function readEnhancedMarkdown(store: MainStore, sessionId: string): string {
const value = store.getCell("sessions", sessionId, "enhanced_md");
return typeof value === "string" ? value : "";
}

function getLanguage(store: MainStore): string {
const value = store.getValue("ai_language");
return typeof value === "string" && value.length > 0 ? value : "en";
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ async function* executeWorkflow(params: {
}
}

async function getSystemPrompt(_args: TaskArgsMapTransformed["title"]) {
const result = await templateCommands.render("title.system", {});
async function getSystemPrompt(args: TaskArgsMapTransformed["title"]) {
const result = await templateCommands.render("title.system", {
language: args.language,
});

if (result.status === "error") {
throw new Error(result.error);
Expand Down
5 changes: 5 additions & 0 deletions crates/template/assets/_language.partial.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% macro enforce_language(language_code, prefix, suffix="language.") -%}
{% if language_code and language_code != "en" -%}
{{ prefix }} {{ language_code }} {{ suffix }}
{% endif -%}
{%- endmacro %}
4 changes: 4 additions & 0 deletions crates/template/assets/chat.system.jinja
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{% from "_language.partial" import enforce_language %}

You are a helpful AI meeting assistant in Hyprnote, an intelligent meeting platform that transcribes
and analyzes meetings. Your purpose is to help users understand their meeting content better.

{{ enforce_language(language, "IMPORTANT: Respond in") }}

You have access to the meeting transcript, AI-generated (enhanced)summary of the meeting, and the original note that the user wrote.

Always keep your responses concise, professional, and directly relevant to the user's questions.
Expand Down
4 changes: 4 additions & 0 deletions crates/template/assets/enhance.system.jinja
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{% from "_language.partial" import enforce_language %}

You are an expert at creating structured, comprehensive meeting summaries.

{{ enforce_language(language, "IMPORTANT: Generate all content in") }}

Format requirements:

- Start with h1 header(#)
Expand Down
3 changes: 3 additions & 0 deletions crates/template/assets/title.system.jinja
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
{% from "_language.partial" import enforce_language %}

You are a professional assistant that generates a perfect title for a meeting note.
{{ enforce_language(language, "Generate the title in") }}
Only output the title as plaintext, nothing else. No characters like *"'([{}]):.
4 changes: 4 additions & 0 deletions crates/template/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub const POSTPROCESS_TRANSCRIPT_SYSTEM_TPL: &str =
#[cfg(not(debug_assertions))]
pub const POSTPROCESS_TRANSCRIPT_USER_TPL: &str =
include_str!("../assets/postprocess_transcript.user.jinja");
#[cfg(not(debug_assertions))]
pub const LANGUAGE_PARTIAL_TPL: &str = include_str!("../assets/_language.partial.jinja");

static GLOBAL_ENV: OnceLock<minijinja::Environment<'static>> = OnceLock::new();

Expand Down Expand Up @@ -128,6 +130,8 @@ fn init_environment() -> minijinja::Environment<'static> {
POSTPROCESS_TRANSCRIPT_USER_TPL,
)
.unwrap();
env.add_template("_language.partial", LANGUAGE_PARTIAL_TPL)
.unwrap();
}

{
Expand Down