Skip to content
Open
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
5 changes: 4 additions & 1 deletion open-sse/executors/qwen.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ export class QwenExecutor extends DefaultExecutor {

transformRequest(model, body, stream, credentials) {
let next = body && typeof body === "object" ? { ...body } : body;
if (stream && next?.messages && !next.stream_options) {
if (stream === false) {
// Non-streaming: strip stream_options entirely (Qwen requires it only when streaming)
delete next.stream_options;
} else if (stream && next?.messages && !next.stream_options) {
next.stream_options = { include_usage: true };
}
next = sanitizeQwenThinkingToolChoice(next);
Expand Down
30 changes: 29 additions & 1 deletion src/app/api/providers/[id]/models/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const PROVIDER_MODELS_CONFIG = {
siliconflow: createOpenAIModelsConfig("https://api.siliconflow.cn/v1/models"),
hyperbolic: createOpenAIModelsConfig("https://api.hyperbolic.xyz/v1/models"),
ollama: createOpenAIModelsConfig("https://ollama.com/api/tags"),
"ollama-local": createOpenAIModelsConfig("http://localhost:11434/api/tags"),
// ollama-local: url resolved dynamically below via providerSpecificData.baseUrl
nanobanana: createOpenAIModelsConfig("https://api.nanobananaapi.ai/v1/models"),
chutes: createOpenAIModelsConfig("https://llm.chutes.ai/v1/models"),
nvidia: createOpenAIModelsConfig("https://integrate.api.nvidia.com/v1/models"),
Expand Down Expand Up @@ -380,6 +380,34 @@ export async function GET(request, { params }) {
});
}

// Handle ollama-local: resolve URL from providerSpecificData.baseUrl if provided,
// otherwise fall back to default localhost address.
if (connection.provider === "ollama-local") {
const baseUrl = connection.providerSpecificData?.baseUrl;
const url = baseUrl
? `${baseUrl.replace(/\/$/, "")}/api/tags`
: "http://localhost:11434/api/tags";
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
if (!response.ok) {
const errorText = await response.text();
console.log(`Error fetching models from ollama-local:`, errorText);
return NextResponse.json(
{ error: `Failed to fetch models: ${response.status}` },
{ status: response.status }
);
}
const data = await response.json();
const models = parseOpenAIStyleModels(data);
return NextResponse.json({
provider: connection.provider,
connectionId: connection.id,
models,
});
}

const config = PROVIDER_MODELS_CONFIG[connection.provider];
if (!config) {
return NextResponse.json(
Expand Down
14 changes: 11 additions & 3 deletions src/shared/components/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname } from "next/navigation";
import { cn } from "@/shared/utils/cn";
import { APP_CONFIG } from "@/shared/constants/config";
import { MEDIA_PROVIDER_KINDS } from "@/shared/constants/providers";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import Button from "./Button";
import { ConfirmModal } from "./Modal";

Expand Down Expand Up @@ -41,6 +42,9 @@ export default function Sidebar({ onClose }) {
const [isDisconnected, setIsDisconnected] = useState(false);
const [updateInfo, setUpdateInfo] = useState(null);
const [enableTranslator, setEnableTranslator] = useState(false);
const { copied, copy } = useCopyToClipboard(2000);

const INSTALL_CMD = "npm install -g 9router@latest";

useEffect(() => {
fetch("/api/settings")
Expand Down Expand Up @@ -100,14 +104,18 @@ export default function Sidebar({ onClose }) {
</div>
</Link>
{updateInfo && (
<div className="flex flex-col gap-0.5">
<button
onClick={() => copy(INSTALL_CMD)}
title="Click to copy install command"
className="flex flex-col gap-0.5 text-left hover:opacity-80 transition-opacity cursor-pointer rounded p-1 -m-1"
>
<span className="text-xs font-semibold text-green-600 dark:text-amber-500">
↑ New version available: v{updateInfo.latestVersion}
</span>
<code className="text-[10px] text-green-600/80 dark:text-amber-400/70 font-mono select-all">
npm install -g 9router@latest
{copied ? "✓ copied!" : INSTALL_CMD}
</code>
</div>
</button>
)}
</div>

Expand Down