Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 41bee19f6b | |||
| f4febf8973 |
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is set at build time via -ldflags, or defaults to dev
|
||||
var Version = "0.4.1"
|
||||
var Version = "0.4.2"
|
||||
|
||||
func getEnvOrDefault(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vessel",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { settingsState } from '$lib/stores/settings.svelte';
|
||||
import { modelsState, type ModelDefaults } from '$lib/stores/models.svelte';
|
||||
import {
|
||||
PARAMETER_RANGES,
|
||||
PARAMETER_LABELS,
|
||||
@@ -16,6 +17,26 @@
|
||||
// Parameter keys for iteration
|
||||
const parameterKeys: (keyof ModelParameters)[] = ['temperature', 'top_k', 'top_p', 'num_ctx'];
|
||||
|
||||
// Track model defaults for the selected model
|
||||
let modelDefaults = $state<ModelDefaults>({});
|
||||
|
||||
// Fetch model defaults when panel opens or model changes
|
||||
$effect(() => {
|
||||
if (settingsState.isPanelOpen && modelsState.selectedId) {
|
||||
modelsState.fetchModelDefaults(modelsState.selectedId).then((defaults) => {
|
||||
modelDefaults = defaults;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the default value for a parameter (from model or hardcoded fallback)
|
||||
*/
|
||||
function getDefaultValue(key: keyof ModelParameters): number {
|
||||
const modelValue = modelDefaults[key];
|
||||
return modelValue ?? DEFAULT_MODEL_PARAMETERS[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a parameter value for display
|
||||
*/
|
||||
@@ -79,7 +100,7 @@
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={settingsState.useCustomParameters}
|
||||
onclick={() => settingsState.toggleCustomParameters()}
|
||||
onclick={() => settingsState.toggleCustomParameters(modelDefaults)}
|
||||
class="relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-theme-secondary {settingsState.useCustomParameters ? 'bg-sky-600' : 'bg-theme-tertiary'}"
|
||||
>
|
||||
<span
|
||||
@@ -93,7 +114,7 @@
|
||||
{#each parameterKeys as key}
|
||||
{@const range = PARAMETER_RANGES[key]}
|
||||
{@const value = getValue(key)}
|
||||
{@const isDefault = value === DEFAULT_MODEL_PARAMETERS[key]}
|
||||
{@const isDefault = value === getDefaultValue(key)}
|
||||
|
||||
<div>
|
||||
<div class="mb-1 flex items-center justify-between">
|
||||
@@ -132,7 +153,7 @@
|
||||
<div class="mt-4 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => settingsState.resetToDefaults()}
|
||||
onclick={() => settingsState.resetToDefaults(modelDefaults)}
|
||||
class="rounded px-2 py-1 text-xs text-theme-muted hover:bg-theme-tertiary hover:text-theme-secondary"
|
||||
>
|
||||
Reset to defaults
|
||||
|
||||
@@ -65,6 +65,14 @@ export interface ModelUpdateStatus {
|
||||
localModifiedAt: string;
|
||||
}
|
||||
|
||||
/** Model default parameters from Ollama */
|
||||
export interface ModelDefaults {
|
||||
temperature?: number;
|
||||
top_k?: number;
|
||||
top_p?: number;
|
||||
num_ctx?: number;
|
||||
}
|
||||
|
||||
/** Models state class with reactive properties */
|
||||
export class ModelsState {
|
||||
// Core state
|
||||
@@ -81,6 +89,10 @@ export class ModelsState {
|
||||
private capabilitiesCache = $state<Map<string, OllamaCapability[]>>(new Map());
|
||||
private capabilitiesFetching = new Set<string>();
|
||||
|
||||
// Model defaults cache: modelName -> default parameters
|
||||
private modelDefaultsCache = $state<Map<string, ModelDefaults>>(new Map());
|
||||
private modelDefaultsFetching = new Set<string>();
|
||||
|
||||
// Derived: Currently selected model
|
||||
selected = $derived.by(() => {
|
||||
if (!this.selectedId) return null;
|
||||
@@ -429,6 +441,99 @@ export class ModelsState {
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Model Defaults
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Parse model parameters from the Ollama show response
|
||||
* The parameters field contains lines like "temperature 0.7" or "num_ctx 4096"
|
||||
*/
|
||||
private parseModelParameters(parametersStr: string): ModelDefaults {
|
||||
const defaults: ModelDefaults = {};
|
||||
|
||||
if (!parametersStr) return defaults;
|
||||
|
||||
const lines = parametersStr.split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
|
||||
// Parse "key value" format
|
||||
const spaceIndex = trimmed.indexOf(' ');
|
||||
if (spaceIndex === -1) continue;
|
||||
|
||||
const key = trimmed.substring(0, spaceIndex).toLowerCase();
|
||||
const value = trimmed.substring(spaceIndex + 1).trim();
|
||||
|
||||
switch (key) {
|
||||
case 'temperature':
|
||||
defaults.temperature = parseFloat(value);
|
||||
break;
|
||||
case 'top_k':
|
||||
defaults.top_k = parseInt(value, 10);
|
||||
break;
|
||||
case 'top_p':
|
||||
defaults.top_p = parseFloat(value);
|
||||
break;
|
||||
case 'num_ctx':
|
||||
defaults.num_ctx = parseInt(value, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch model defaults from Ollama /api/show
|
||||
*/
|
||||
async fetchModelDefaults(modelName: string): Promise<ModelDefaults> {
|
||||
// Check cache first
|
||||
const cached = this.modelDefaultsCache.get(modelName);
|
||||
if (cached) return cached;
|
||||
|
||||
// Avoid duplicate fetches
|
||||
if (this.modelDefaultsFetching.has(modelName)) {
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
return this.modelDefaultsCache.get(modelName) ?? {};
|
||||
}
|
||||
|
||||
this.modelDefaultsFetching.add(modelName);
|
||||
|
||||
try {
|
||||
const response = await ollamaClient.showModel(modelName);
|
||||
const defaults = this.parseModelParameters(response.parameters);
|
||||
|
||||
// Update cache reactively
|
||||
const newCache = new Map(this.modelDefaultsCache);
|
||||
newCache.set(modelName, defaults);
|
||||
this.modelDefaultsCache = newCache;
|
||||
|
||||
return defaults;
|
||||
} catch (err) {
|
||||
console.warn(`Failed to fetch defaults for ${modelName}:`, err);
|
||||
return {};
|
||||
} finally {
|
||||
this.modelDefaultsFetching.delete(modelName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached model defaults (returns empty if not fetched)
|
||||
*/
|
||||
getModelDefaults(modelName: string): ModelDefaults {
|
||||
return this.modelDefaultsCache.get(modelName) ?? {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get defaults for selected model
|
||||
*/
|
||||
get selectedModelDefaults(): ModelDefaults {
|
||||
if (!this.selectedId) return {};
|
||||
return this.modelDefaultsCache.get(this.selectedId) ?? {};
|
||||
}
|
||||
}
|
||||
|
||||
/** Singleton models state instance */
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
DEFAULT_CHAT_SETTINGS,
|
||||
PARAMETER_RANGES
|
||||
} from '$lib/types/settings';
|
||||
import type { ModelDefaults } from './models.svelte';
|
||||
|
||||
const STORAGE_KEY = 'vessel-settings';
|
||||
|
||||
@@ -79,12 +80,30 @@ export class SettingsState {
|
||||
|
||||
/**
|
||||
* Toggle whether to use custom parameters
|
||||
* When enabling, optionally initialize from model defaults
|
||||
*/
|
||||
toggleCustomParameters(): void {
|
||||
toggleCustomParameters(modelDefaults?: ModelDefaults): void {
|
||||
this.useCustomParameters = !this.useCustomParameters;
|
||||
|
||||
// When enabling custom parameters, initialize from model defaults if provided
|
||||
if (this.useCustomParameters && modelDefaults) {
|
||||
this.initializeFromModelDefaults(modelDefaults);
|
||||
}
|
||||
|
||||
this.saveToStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize parameters from model defaults
|
||||
* Falls back to hardcoded defaults for any missing values
|
||||
*/
|
||||
initializeFromModelDefaults(modelDefaults: ModelDefaults): void {
|
||||
this.temperature = modelDefaults.temperature ?? DEFAULT_MODEL_PARAMETERS.temperature;
|
||||
this.top_k = modelDefaults.top_k ?? DEFAULT_MODEL_PARAMETERS.top_k;
|
||||
this.top_p = modelDefaults.top_p ?? DEFAULT_MODEL_PARAMETERS.top_p;
|
||||
this.num_ctx = modelDefaults.num_ctx ?? DEFAULT_MODEL_PARAMETERS.num_ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single parameter
|
||||
*/
|
||||
@@ -112,14 +131,13 @@ export class SettingsState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all parameters to defaults
|
||||
* Reset all parameters to model defaults (or hardcoded defaults if not available)
|
||||
*/
|
||||
resetToDefaults(): void {
|
||||
this.temperature = DEFAULT_MODEL_PARAMETERS.temperature;
|
||||
this.top_k = DEFAULT_MODEL_PARAMETERS.top_k;
|
||||
this.top_p = DEFAULT_MODEL_PARAMETERS.top_p;
|
||||
this.num_ctx = DEFAULT_MODEL_PARAMETERS.num_ctx;
|
||||
this.useCustomParameters = false;
|
||||
resetToDefaults(modelDefaults?: ModelDefaults): void {
|
||||
this.temperature = modelDefaults?.temperature ?? DEFAULT_MODEL_PARAMETERS.temperature;
|
||||
this.top_k = modelDefaults?.top_k ?? DEFAULT_MODEL_PARAMETERS.top_k;
|
||||
this.top_p = modelDefaults?.top_p ?? DEFAULT_MODEL_PARAMETERS.top_p;
|
||||
this.num_ctx = modelDefaults?.num_ctx ?? DEFAULT_MODEL_PARAMETERS.num_ctx;
|
||||
this.saveToStorage();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user