forked from open-webui/open-webui
Merge pull request #548 from ollama-webui/custom-interface
feat: custom interface support
This commit is contained in:
commit
e2ec19b472
9 changed files with 231 additions and 73 deletions
|
@ -11,14 +11,15 @@ from apps.web.routers import (
|
||||||
configs,
|
configs,
|
||||||
utils,
|
utils,
|
||||||
)
|
)
|
||||||
from config import WEBUI_VERSION, WEBUI_AUTH
|
from config import WEBUI_VERSION, WEBUI_AUTH, DEFAULT_MODELS, DEFAULT_PROMPT_SUGGESTIONS
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
origins = ["*"]
|
origins = ["*"]
|
||||||
|
|
||||||
app.state.ENABLE_SIGNUP = True
|
app.state.ENABLE_SIGNUP = True
|
||||||
app.state.DEFAULT_MODELS = None
|
app.state.DEFAULT_MODELS = DEFAULT_MODELS
|
||||||
|
app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
|
@ -46,4 +47,5 @@ async def get_status():
|
||||||
"version": WEBUI_VERSION,
|
"version": WEBUI_VERSION,
|
||||||
"auth": WEBUI_AUTH,
|
"auth": WEBUI_AUTH,
|
||||||
"default_models": app.state.DEFAULT_MODELS,
|
"default_models": app.state.DEFAULT_MODELS,
|
||||||
|
"default_prompt_suggestions": app.state.DEFAULT_PROMPT_SUGGESTIONS,
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,24 @@ class SetDefaultModelsForm(BaseModel):
|
||||||
models: str
|
models: str
|
||||||
|
|
||||||
|
|
||||||
|
class PromptSuggestion(BaseModel):
|
||||||
|
title: List[str]
|
||||||
|
content: str
|
||||||
|
|
||||||
|
|
||||||
|
class SetDefaultSuggestionsForm(BaseModel):
|
||||||
|
suggestions: List[PromptSuggestion]
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# SetDefaultModels
|
# SetDefaultModels
|
||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
@router.post("/default/models", response_model=str)
|
@router.post("/default/models", response_model=str)
|
||||||
async def set_global_default_models(request: Request,
|
async def set_global_default_models(
|
||||||
form_data: SetDefaultModelsForm,
|
request: Request, form_data: SetDefaultModelsForm, user=Depends(get_current_user)
|
||||||
user=Depends(get_current_user)):
|
):
|
||||||
if user.role == "admin":
|
if user.role == "admin":
|
||||||
request.app.state.DEFAULT_MODELS = form_data.models
|
request.app.state.DEFAULT_MODELS = form_data.models
|
||||||
return request.app.state.DEFAULT_MODELS
|
return request.app.state.DEFAULT_MODELS
|
||||||
|
@ -38,3 +47,20 @@ async def set_global_default_models(request: Request,
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/default/suggestions", response_model=List[PromptSuggestion])
|
||||||
|
async def set_global_default_suggestions(
|
||||||
|
request: Request,
|
||||||
|
form_data: SetDefaultSuggestionsForm,
|
||||||
|
user=Depends(get_current_user),
|
||||||
|
):
|
||||||
|
if user.role == "admin":
|
||||||
|
data = form_data.model_dump()
|
||||||
|
request.app.state.DEFAULT_PROMPT_SUGGESTIONS = data["suggestions"]
|
||||||
|
return request.app.state.DEFAULT_PROMPT_SUGGESTIONS
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
||||||
|
)
|
||||||
|
|
|
@ -54,6 +54,34 @@ OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "")
|
||||||
if OPENAI_API_BASE_URL == "":
|
if OPENAI_API_BASE_URL == "":
|
||||||
OPENAI_API_BASE_URL = "https://api.openai.com/v1"
|
OPENAI_API_BASE_URL = "https://api.openai.com/v1"
|
||||||
|
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# WEBUI
|
||||||
|
####################################
|
||||||
|
|
||||||
|
DEFAULT_MODELS = os.environ.get("DEFAULT_MODELS", None)
|
||||||
|
DEFAULT_PROMPT_SUGGESTIONS = os.environ.get(
|
||||||
|
"DEFAULT_PROMPT_SUGGESTIONS",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": ["Help me study", "vocabulary for a college entrance exam"],
|
||||||
|
"content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": ["Give me ideas", "for what to do with my kids' art"],
|
||||||
|
"content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": ["Tell me a fun fact", "about the Roman Empire"],
|
||||||
|
"content": "Tell me a random fun fact about the Roman Empire",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": ["Show me a code snippet", "of a website's sticky header"],
|
||||||
|
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
####################################
|
####################################
|
||||||
# WEBUI_VERSION
|
# WEBUI_VERSION
|
||||||
####################################
|
####################################
|
||||||
|
|
|
@ -29,3 +29,33 @@ export const setDefaultModels = async (token: string, models: string) => {
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setDefaultPromptSuggestions = async (token: string, promptSuggestions: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/suggestions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
suggestions: promptSuggestions
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col text-left self-center">
|
<div class="flex flex-col text-left self-center">
|
||||||
{#if prompt.title}
|
{#if prompt.title && prompt.title[0] !== ''}
|
||||||
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
|
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
|
||||||
<div class="text-sm text-gray-500">{prompt.title[1]}</div>
|
<div class="text-sm text-gray-500">{prompt.title[1]}</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
updateOpenAIUrl
|
updateOpenAIUrl
|
||||||
} from '$lib/apis/openai';
|
} from '$lib/apis/openai';
|
||||||
import { resetVectorDB } from '$lib/apis/rag';
|
import { resetVectorDB } from '$lib/apis/rag';
|
||||||
|
import { setDefaultPromptSuggestions } from '$lib/apis/configs';
|
||||||
|
import { getBackendConfig } from '$lib/apis';
|
||||||
|
|
||||||
export let show = false;
|
export let show = false;
|
||||||
|
|
||||||
|
@ -99,6 +101,9 @@
|
||||||
let OPENAI_API_KEY = '';
|
let OPENAI_API_KEY = '';
|
||||||
let OPENAI_API_BASE_URL = '';
|
let OPENAI_API_BASE_URL = '';
|
||||||
|
|
||||||
|
// Interface
|
||||||
|
let promptSuggestions = [];
|
||||||
|
|
||||||
// Addons
|
// Addons
|
||||||
let titleAutoGenerate = true;
|
let titleAutoGenerate = true;
|
||||||
let speechAutoSend = false;
|
let speechAutoSend = false;
|
||||||
|
@ -191,6 +196,11 @@
|
||||||
await models.set(await getModels());
|
await models.set(await getModels());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateInterfaceHandler = async () => {
|
||||||
|
promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
|
||||||
|
await config.set(await getBackendConfig());
|
||||||
|
};
|
||||||
|
|
||||||
const toggleTheme = async () => {
|
const toggleTheme = async () => {
|
||||||
if (theme === 'dark') {
|
if (theme === 'dark') {
|
||||||
theme = 'light';
|
theme = 'light';
|
||||||
|
@ -577,6 +587,7 @@
|
||||||
API_BASE_URL = await getOllamaAPIUrl(localStorage.token);
|
API_BASE_URL = await getOllamaAPIUrl(localStorage.token);
|
||||||
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
|
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
|
||||||
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
|
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
|
||||||
|
promptSuggestions = $config?.default_prompt_suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||||
|
@ -745,6 +756,32 @@
|
||||||
</div>
|
</div>
|
||||||
<div class=" self-center">External</div>
|
<div class=" self-center">External</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||||
|
'interface'
|
||||||
|
? 'bg-gray-200 dark:bg-gray-700'
|
||||||
|
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||||
|
on:click={() => {
|
||||||
|
selectedTab = 'interface';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" self-center mr-2">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class=" self-center">Interface</div>
|
||||||
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -797,34 +834,6 @@
|
||||||
<div class=" self-center">Chats</div>
|
<div class=" self-center">Chats</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if !$config || ($config && !$config.auth)}
|
|
||||||
<button
|
|
||||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
|
||||||
'auth'
|
|
||||||
? 'bg-gray-200 dark:bg-gray-700'
|
|
||||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
|
||||||
on:click={() => {
|
|
||||||
selectedTab = 'auth';
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class=" self-center mr-2">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
class="w-4 h-4"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M12.516 2.17a.75.75 0 00-1.032 0 11.209 11.209 0 01-7.877 3.08.75.75 0 00-.722.515A12.74 12.74 0 002.25 9.75c0 5.942 4.064 10.933 9.563 12.348a.749.749 0 00.374 0c5.499-1.415 9.563-6.406 9.563-12.348 0-1.39-.223-2.73-.635-3.985a.75.75 0 00-.722-.516l-.143.001c-2.996 0-5.717-1.17-7.734-3.08zm3.094 8.016a.75.75 0 10-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 00-1.06 1.06l2.25 2.25a.75.75 0 001.14-.094l3.75-5.25z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class=" self-center">Authentication</div>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||||
'account'
|
'account'
|
||||||
|
@ -877,7 +886,7 @@
|
||||||
<div class=" self-center">About</div>
|
<div class=" self-center">About</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 md:min-h-[340px]">
|
<div class="flex-1 md:min-h-[380px]">
|
||||||
{#if selectedTab === 'general'}
|
{#if selectedTab === 'general'}
|
||||||
<div class="flex flex-col space-y-3">
|
<div class="flex flex-col space-y-3">
|
||||||
<div>
|
<div>
|
||||||
|
@ -1048,7 +1057,7 @@
|
||||||
</div>
|
</div>
|
||||||
{:else if selectedTab === 'advanced'}
|
{:else if selectedTab === 'advanced'}
|
||||||
<div class="flex flex-col h-full justify-between text-sm">
|
<div class="flex flex-col h-full justify-between text-sm">
|
||||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-72">
|
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||||
<div class=" text-sm font-medium">Parameters</div>
|
<div class=" text-sm font-medium">Parameters</div>
|
||||||
|
|
||||||
<Advanced bind:options />
|
<Advanced bind:options />
|
||||||
|
@ -1483,6 +1492,103 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||||
|
<button
|
||||||
|
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{:else if selectedTab === 'interface'}
|
||||||
|
<form
|
||||||
|
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||||
|
on:submit|preventDefault={() => {
|
||||||
|
updateInterfaceHandler();
|
||||||
|
show = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||||
|
<div class="flex w-full justify-between mb-2">
|
||||||
|
<div class=" self-center text-sm font-semibold">Default Prompt Suggestions</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-1 px-3 text-xs flex rounded transition"
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') {
|
||||||
|
promptSuggestions = [...promptSuggestions, { content: '', title: ['', ''] }];
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col space-y-1">
|
||||||
|
{#each promptSuggestions as prompt, promptIdx}
|
||||||
|
<div class=" flex border dark:border-gray-600 rounded-lg">
|
||||||
|
<div class="flex flex-col flex-1">
|
||||||
|
<div class="flex border-b dark:border-gray-600 w-full">
|
||||||
|
<input
|
||||||
|
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||||
|
placeholder="Title (e.g. Tell me a fun fact)"
|
||||||
|
bind:value={prompt.title[0]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||||
|
placeholder="Subtitle (e.g. about the Roman Empire)"
|
||||||
|
bind:value={prompt.title[1]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||||
|
placeholder="Prompt (e.g. Tell me a fun fact about the Roman Empire)"
|
||||||
|
bind:value={prompt.content}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="px-2"
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
promptSuggestions.splice(promptIdx, 1);
|
||||||
|
promptSuggestions = promptSuggestions;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if promptSuggestions.length > 0}
|
||||||
|
<div class="text-xs text-left w-full mt-2">
|
||||||
|
Adjusting these settings will apply changes universally to all users.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
} else if (size === 'sm') {
|
} else if (size === 'sm') {
|
||||||
return 'w-[30rem]';
|
return 'w-[30rem]';
|
||||||
} else {
|
} else {
|
||||||
return 'w-[40rem]';
|
return 'w-[42rem]';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -765,24 +765,7 @@
|
||||||
bind:files
|
bind:files
|
||||||
bind:prompt
|
bind:prompt
|
||||||
bind:autoScroll
|
bind:autoScroll
|
||||||
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
|
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
||||||
{
|
|
||||||
title: ['Help me study', 'vocabulary for a college entrance exam'],
|
|
||||||
content: `Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Give me ideas', `for what to do with my kids' art`],
|
|
||||||
content: `What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Tell me a fun fact', 'about the Roman Empire'],
|
|
||||||
content: 'Tell me a random fun fact about the Roman Empire'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Show me a code snippet', `of a website's sticky header`],
|
|
||||||
content: `Show me a code snippet of a website's sticky header in CSS and JavaScript.`
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
{messages}
|
{messages}
|
||||||
{submitPrompt}
|
{submitPrompt}
|
||||||
{stopResponse}
|
{stopResponse}
|
||||||
|
|
|
@ -794,24 +794,7 @@
|
||||||
bind:files
|
bind:files
|
||||||
bind:prompt
|
bind:prompt
|
||||||
bind:autoScroll
|
bind:autoScroll
|
||||||
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
|
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
||||||
{
|
|
||||||
title: ['Help me study', 'vocabulary for a college entrance exam'],
|
|
||||||
content: `Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Give me ideas', `for what to do with my kids' art`],
|
|
||||||
content: `What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Tell me a fun fact', 'about the Roman Empire'],
|
|
||||||
content: 'Tell me a random fun fact about the Roman Empire'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: ['Show me a code snippet', `of a website's sticky header`],
|
|
||||||
content: `Show me a code snippet of a website's sticky header in CSS and JavaScript.`
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
{messages}
|
{messages}
|
||||||
{submitPrompt}
|
{submitPrompt}
|
||||||
{stopResponse}
|
{stopResponse}
|
||||||
|
|
Loading…
Reference in a new issue