Skip to content

Commit

Permalink
Fixes and new custom provider presets
Browse files Browse the repository at this point in the history
  • Loading branch information
metaspartan committed Jan 3, 2025
1 parent 8d8aba5 commit 8fb5591
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 29 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ Curiso AI is an infinite canvas for your thoughts—a platform that seamlessly c
- xAI
- Groq
- OpenRouter

- **Local AI Inference Provider Integration**:
- Ollama
- Exo
- Jan.ai
- LM Studio

- **Custom Model Support**: Add and configure custom AI models
- **Inference Parameters**: Customize the inference parameters for your conversations
- **Multiple Boards**: Create and manage multiple workspaces
Expand All @@ -29,6 +35,8 @@ Curiso AI is an infinite canvas for your thoughts—a platform that seamlessly c

![screenshot2](screenshot2.png)

![screenshot3](custommodels.png)

## Donate

If you find Curiso.ai useful, please consider donating to support its development.
Expand Down Expand Up @@ -111,3 +119,6 @@ Carsen Klock - [@metaspartan](https://github.com/metaspartan) [@carsenklock](htt
- [xAI](https://x.com/)
- [Groq](https://www.groq.com/)
- [OpenRouter](https://openrouter.ai/)
- [Jan.ai](https://jan.ai/)
- [LM Studio](https://lmstudio.ai/)
- [Exo](https://github.com/exo-explore/exo)
Binary file added client/src/assets/exo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions client/src/assets/jan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/src/assets/lmstudio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 74 additions & 8 deletions client/src/components/AddModelDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import {
import { CustomModel } from "@/lib/types";
import { useStore } from "@/lib/store";
import logo from "@/assets/logo.svg"

import { PRESET_ENDPOINTS } from "@/lib/constants";

interface AddModelDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}

export function AddModelDialog({ open, onOpenChange }: AddModelDialogProps) {
const [isCustomEndpoint, setIsCustomEndpoint] = useState(true);
const { settings, setSettings } = useStore();
const [newModel, setNewModel] = useState<Partial<CustomModel>>({
provider: 'openai',
Expand Down Expand Up @@ -57,7 +59,77 @@ import {
<div className="space-y-4">
<div className="grid gap-4">
<div className="space-y-2">
<Label>Model Details</Label>
<div className="space-y-2">
<Label>Endpoint Type</Label>
<Select
value={newModel.endpoint || "custom"}
onValueChange={(value) => {
const preset = PRESET_ENDPOINTS.find(p => p.url === value);
setIsCustomEndpoint(value === "custom");
setNewModel({
...newModel,
endpoint: value === "custom" ? "" : value,
name: preset && preset.name !== "Custom" ? `${preset.name} Model` : newModel.name,
requiresAuth: preset?.name === "Custom"
});
}}
>
<SelectTrigger>
<SelectValue placeholder="Select endpoint type">
{newModel.endpoint && (
<div className="flex items-start gap-2">
<img
src={PRESET_ENDPOINTS.find(p => p.url === newModel.endpoint)?.icon}
alt={PRESET_ENDPOINTS.find(p => p.url === newModel.endpoint)?.name}
className="w-5 h-5 object-contain"
/>
<div className="flex flex-col">
<span>{PRESET_ENDPOINTS.find(p => p.url === newModel.endpoint)?.name}</span>
<span className="text-xs text-muted-foreground">
{PRESET_ENDPOINTS.find(p => p.url === newModel.endpoint)?.description}
</span>
</div>
</div>
)}
</SelectValue>
</SelectTrigger>
<SelectContent>
{PRESET_ENDPOINTS.map((preset) => (
<SelectItem key={preset.url} value={preset.url || "custom"}>
<div className="flex items-center gap-2">
<img
src={preset.icon}
alt={preset.name}
className="w-6 h-6 object-contain"
/>
<div className="flex flex-col">
<span>{preset.name}</span>
<span className="text-xs text-muted-foreground">
{preset.description}
</span>
</div>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>

{isCustomEndpoint && (
<div className="space-y-2">
<Label>Custom Endpoint URL</Label>
<Input
placeholder="https://localhost:11434/v1"
value={newModel.endpoint}
onChange={(e) => setNewModel({
...newModel,
endpoint: e.target.value
})}
/>
</div>
)}
</div>
<Label>Model Details</Label>
<Input
value={newModel.name || ''}
onChange={(e) => setNewModel({ ...newModel, name: e.target.value })}
Expand All @@ -68,12 +140,6 @@ import {
onChange={(e) => setNewModel({ ...newModel, id: e.target.value })}
placeholder="Model ID (llama3.2:1b)"
/>
<Input
value={newModel.endpoint || ''}
onChange={(e) => setNewModel({ ...newModel, endpoint: e.target.value })}
placeholder="API Endpoint (http://localhost:11434/v1)"
/>
</div>
<div className="space-y-2">
<Label>Provider</Label>
<Select
Expand Down
28 changes: 20 additions & 8 deletions client/src/components/ChatNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import { Copy, Check } from "lucide-react";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import { NodeResizer } from 'reactflow';
import Anthropic from '@anthropic-ai/sdk';
import { DEFAULT_AI_SETTINGS, AISettings } from '@/lib/constants';
import { DEFAULT_AI_SETTINGS, AISettings, PRESET_ENDPOINTS } from '@/lib/constants';
import { ImageUpload } from "./ImageUpload";

import logo from "@/assets/logo.svg";

export function ChatNode({ id, data: initialData }: NodeProps) {
const [input, setInput] = useState("");
Expand Down Expand Up @@ -565,10 +565,14 @@ export function ChatNode({ id, data: initialData }: NodeProps) {
<SelectTrigger className="w-[340px] h-8">
<SelectValue placeholder="Select model">
<div className="flex items-center gap-2">
<img
src={currentModel?.thumbnailUrl}
alt={currentModel?.name}
className="w-4 h-4"
<img
src={
currentModel && 'endpoint' in currentModel
? PRESET_ENDPOINTS.find(p => p.url === currentModel.endpoint)?.icon || currentModel.thumbnailUrl || logo
: currentModel?.thumbnailUrl || logo
}
alt={currentModel?.name}
className="w-4 h-4"
/>
<span className="text-xs">{currentModel?.name}</span>
</div>
Expand All @@ -578,13 +582,21 @@ export function ChatNode({ id, data: initialData }: NodeProps) {
{[...availableModels, ...(settings.customModels || [])].map((m) => (
<SelectItem key={m.id} value={m.id}>
<div className="flex items-center gap-2">
<img src={m.thumbnailUrl} alt={m.name} className="w-4 h-4" />
<img
src={
'endpoint' in m
? PRESET_ENDPOINTS.find(p => p.url === m.endpoint)?.icon || m.thumbnailUrl || logo
: m.thumbnailUrl || logo
}
alt={m.name}
className="w-4 h-4"
/>
<span>{m.name}</span>
{'endpoint' in m && <span className="text-xs text-muted-foreground">(Custom)</span>}
</div>
</SelectItem>
))}
</SelectContent>
</SelectContent>
</Select>
<div className="flex gap-1">
<Button
Expand Down
13 changes: 11 additions & 2 deletions client/src/components/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Info } from "lucide-react";
import logo from "@/assets/logo.svg"
import { PRESET_ENDPOINTS } from "@/lib/constants";
interface ModelSelectorProps {
models: AIModel[];
selectedModel: string;
Expand All @@ -16,6 +17,14 @@ export function ModelSelector({ models, selectedModel, onSelect }: ModelSelector
const settings = useStore((state) => state.settings);
const allModels = [...models, ...(settings.customModels || [])];

const getEndpointIcon = (model: AIModel | CustomModel) => {
if ('endpoint' in model) {
const preset = PRESET_ENDPOINTS.find(p => p.url === model.endpoint);
return preset?.icon;
}
return null;
};

const getModelAuthStatus = (model: AIModel | CustomModel) => {
if ('requiresAuth' in model) {
const customModel = model as CustomModel;
Expand Down Expand Up @@ -53,8 +62,8 @@ export function ModelSelector({ models, selectedModel, onSelect }: ModelSelector
<CardContent className="p-4 space-y-2">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<img
src={model.thumbnailUrl || logo}
<img
src={getEndpointIcon(model) || model.thumbnailUrl || logo}
alt={model.name}
className="w-8 h-8 [&>path]:text-foreground"
/>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {

<div className="flex justify-center">
<img src={logo} alt="Curiso.ai" title="Curiso.ai" className="w-12 h-12" /></div>
<div className="flex justify-center"><p className="text-sm text-muted-foreground justify-center mb-2">Version v1.0.7 by Carsen Klock</p></div>
<div className="flex justify-center"><p className="text-sm text-muted-foreground justify-center mb-2">Version v1.0.8 by Carsen Klock</p></div>
<strong>Curiso.ai</strong> is an infinite canvas for your thoughts—a platform that seamlessly connects nodes and AI services so you can explore ideas in depth without repeating yourself. By guiding the direction of each conversation, Curiso.ai empowers advanced users to unlock richer, more accurate AI interactions.
</div>
<div className="space-y-2 mt-2">
Expand Down
40 changes: 39 additions & 1 deletion client/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import ollamaLogo from "@/assets/ollama-logo.svg";
import exoLogo from "@/assets/exo.png";
import janLogo from "@/assets/jan.svg";
import lmstudioLogo from "@/assets/lmstudio.png";

export const themeColors = [
// Blues & Teals
{ name: 'Teal', value: 'hsl(183 31% 26%)' },
Expand Down Expand Up @@ -32,4 +37,37 @@ export const DEFAULT_AI_SETTINGS = {
presence_penalty: 0
};

export type AISettings = typeof DEFAULT_AI_SETTINGS;
export type AISettings = typeof DEFAULT_AI_SETTINGS;

export const PRESET_ENDPOINTS = [
{
name: "Exo",
url: "http://localhost:52415/v1",
description: "Local Exo instance",
icon: exoLogo
},
{
name: "Ollama",
url: "http://localhost:11434/v1",
description: "Local Ollama instance",
icon: ollamaLogo
},
{
name: "Jan.ai",
url: "http://localhost:1337/v1",
description: "Local Jan.ai instance",
icon: janLogo
},
{
name: "LM Studio",
url: "http://localhost:1234/v1",
description: "Local LM Studio instance",
icon: lmstudioLogo
},
{
name: "Custom",
url: "",
description: "Custom endpoint URL",
icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpath d='M12 8v8'%3E%3C/path%3E%3Cpath d='M8 12h8'%3E%3C/path%3E%3C/svg%3E"
}
] as const;
10 changes: 5 additions & 5 deletions client/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function Flow({ settingsOpen, setSettingsOpen }: { settingsOpen: boolean; setSet
// Update UI immediately
useStore.setState({ settings: newSettings });
// Debounce storage update
debouncedSetSettings(newSettings);
// debouncedSetSettings(newSettings);
};

const onEdgesChange = (changes: EdgeChange[]) => {
Expand All @@ -67,8 +67,8 @@ function Flow({ settingsOpen, setSettingsOpen }: { settingsOpen: boolean; setSet

// Update UI immediately
useStore.setState({ settings: newSettings });
// Debounce storage update
debouncedSetSettings(newSettings);
// // Debounce storage update
// debouncedSetSettings(newSettings);
};

const onConnect = (connection: Connection) => {
Expand Down Expand Up @@ -104,8 +104,8 @@ function Flow({ settingsOpen, setSettingsOpen }: { settingsOpen: boolean; setSet

// Update UI immediately
useStore.setState({ settings: newSettings });
// Debounce storage update
debouncedSetSettings(newSettings);
// // Debounce storage update
// debouncedSetSettings(newSettings);
};

useEffect(() => {
Expand Down
Binary file added custommodels.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "curiso.ai",
"version": "1.0.7",
"version": "1.0.8",
"author": "Carsen Klock",
"type": "module",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "curiso-ai"
version = "1.0.7"
version = "1.0.8"
description = "Curiso AI Desktop"
authors = ["Carsen Klock"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"identifier": "com.curiso.ai",
"productName": "Curiso AI",
"version": "1.0.7",
"version": "1.0.8",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
Expand Down

0 comments on commit 8fb5591

Please sign in to comment.