Compare commits

..

3 Commits

Author SHA1 Message Date
dayuan.jiang
0956684b8e chore: refresh SUGGESTED_MODELS for all providers
Updates the SUGGESTED_MODELS quick-add list in lib/types/model-config.ts
against each provider's official model docs as of 2026-06-09.

- openai: GPT-5.5 / 5.4 frontier; drop deprecated 5.0-5.2 family
- anthropic: add Opus 4.8/4.7/4.6, Sonnet 4.6, Haiku 4.5 (new dateless
  pinned IDs); fix wrong date suffixes on Opus/Sonnet 4.5
- google / vertexai: adopt Gemini 3 family; drop Gemini 2.0 (shut down)
  and 1.5
- azure: GPT-5.x line + o3 / o4-mini; drop gpt-4-turbo / gpt-35-turbo
- bedrock: Opus 4.8/4.7/4.6, Sonnet 4.6, Haiku 4.5, Nova Premier /
  Nova 2 Lite, Llama 4 Maverick / Scout, Mistral Large 3, Pixtral
- openrouter: refreshed against live /api/v1/models
- deepseek: V4 Pro / Flash
- siliconflow / modelscope: DeepSeek V4, Qwen 3.x, drop bogus
  qwen3.5-plus
- gateway: verified against live Vercel AI Gateway endpoint
- doubao: Seed 2.0 / 1.8 / 1.6 in official dash-form IDs
- minimax: + M2.5
- novita: M3, GLM-5.1, Kimi-K2.6, DeepSeek V4
2026-06-09 21:08:02 +09:00
chaochaoweb3
410993a3bf fix: block private IPv6 URLs (#858)
* fix: block private IPv6 URLs

* fix: cover full fe80::/10 link-local range and :: unspecified

- Replace startsWith("fe80:") with a check covering the full fe80::/10
  range (fe80 through febf) per RFC 4291.
- Add :: (unspecified) to the localhost block.
- Drop the dead 0:0:0:0:0:0:0:1 branch (URL parser normalizes it to ::1).
- Add tests for fe9f::1, febf::1, and ::.

---------

Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
2026-06-06 00:30:20 +09:00
Dayuan Jiang
77e7766f9a fix(deps): bump @ai-sdk/amazon-bedrock to 4.0.113 to fix tool streaming under Zod v4 (#860)
The installed 4.0.64 declared `input: z.unknown()` on BedrockToolUseSchema,
which Zod v4 treats as non-optional. With the
`fine-grained-tool-streaming-2025-05-14` beta enabled in lib/ai-providers.ts,
Bedrock's contentBlockStart event arrives without an `input` field, causing
type validation to fail with "expected nonoptional, received undefined".

4.0.101 fixed this upstream by marking input optional on the streaming
tool-use schema. The semver range `^4.0.1` already permitted this; only the
lockfile needed refreshing.

Closes #859
2026-06-06 00:15:45 +09:00
3 changed files with 168 additions and 100 deletions

View File

@@ -11,17 +11,38 @@ export function isPrivateUrl(urlString: string): boolean {
const url = new URL(urlString)
// Strip a trailing dot so FQDN forms like "localhost." (which still
// resolve to 127.0.0.1) cannot bypass the equality checks below.
const hostname = url.hostname.toLowerCase().replace(/\.$/, "")
const hostname = url.hostname
.toLowerCase()
.replace(/^\[|\]$/g, "")
.replace(/\.$/, "")
// Block localhost
if (
hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname === "::1"
hostname === "::1" ||
hostname === "::"
) {
return true
}
// Block IPv6 unique-local (fc00::/7), link-local (fe80::/10),
// and IPv4-mapped (::ffff:0:0/96) hosts.
if (hostname.includes(":")) {
if (
hostname.startsWith("fc") ||
hostname.startsWith("fd") ||
hostname.startsWith("::ffff:")
) {
return true
}
const linkLocal = hostname.match(/^fe([0-9a-f]{2}):/)
if (linkLocal) {
const high = parseInt(linkLocal[1], 16)
if (high >= 0x80 && high <= 0xbf) return true
}
}
// Block AWS/cloud metadata endpoints
if (
hostname === "169.254.169.254" ||

View File

@@ -190,176 +190,202 @@ export const PROVIDER_INFO: Record<
// Suggested models per provider for quick add
export const SUGGESTED_MODELS: Partial<Record<ProviderName, string[]>> = {
openai: [
"gpt-5.2-pro",
"gpt-5.2-chat-latest",
"gpt-5.2",
"gpt-5.1-codex-mini",
"gpt-5.1-codex",
"gpt-5.1-chat-latest",
"gpt-5.1",
"gpt-5-pro",
"gpt-5",
"gpt-5-mini",
"gpt-5-nano",
"gpt-5-codex",
"gpt-5-chat-latest",
"gpt-5.5-pro",
"gpt-5.5",
"gpt-5.4-pro",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-5-codex-mini",
"gpt-4.1",
"gpt-4.1-mini",
"gpt-4.1-nano",
"gpt-4o",
"gpt-4o-mini",
],
anthropic: [
// Claude 4.5 series (latest)
"claude-opus-4-5-20250514",
"claude-sonnet-4-5-20250514",
// Claude 4 series
"claude-opus-4-20250514",
"claude-sonnet-4-20250514",
// Claude 4.8 / 4.7 / 4.6 series (latest, dateless pinned IDs)
"claude-opus-4-8",
"claude-sonnet-4-6",
"claude-haiku-4-5",
"claude-opus-4-7",
"claude-opus-4-6",
// Claude 4.5 series
"claude-sonnet-4-5-20250929",
"claude-opus-4-5-20251101",
// Claude 3.7 series
"claude-3-7-sonnet-20250219",
// Claude 3.5 series
"claude-3-5-sonnet-20241022",
"claude-3-5-haiku-20241022",
// Claude 3 series
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
],
google: [
// Gemini 3 series
"gemini-3.1-pro",
"gemini-3.5-flash",
"gemini-3-flash",
"gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
"gemini-2.5-flash-preview-05-20",
// Gemini 2.0 series
"gemini-2.0-flash",
"gemini-2.0-flash-exp",
"gemini-2.0-flash-lite",
// Gemini 1.5 series
"gemini-1.5-pro",
"gemini-1.5-flash",
// Legacy
"gemini-pro",
"gemini-2.5-flash-lite",
],
vertexai: [
// Gemini 3 series
"gemini-3.1-pro-preview",
"gemini-3.5-flash",
"gemini-3-flash-preview",
"gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
// Gemini 2.0 series
"gemini-2.0-flash",
"gemini-2.0-flash-exp",
// Gemini 1.5 series
"gemini-1.5-pro",
"gemini-1.5-flash",
"gemini-2.5-flash-lite",
],
azure: [
"gpt-5.5",
"gpt-5.4",
"gpt-5.1",
"gpt-5",
"gpt-5-mini",
"gpt-4.1",
"gpt-4o",
"gpt-4o-mini",
"o3",
"o4-mini",
],
azure: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-35-turbo"],
bedrock: [
// Anthropic Claude
"anthropic.claude-opus-4-5-20250514-v1:0",
"anthropic.claude-sonnet-4-5-20250514-v1:0",
"anthropic.claude-opus-4-8",
"anthropic.claude-opus-4-7",
"anthropic.claude-sonnet-4-6",
"anthropic.claude-opus-4-6-v1",
"anthropic.claude-opus-4-5-20251101-v1:0",
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"anthropic.claude-haiku-4-5-20251001-v1:0",
"anthropic.claude-opus-4-1-20250805-v1:0",
"anthropic.claude-opus-4-20250514-v1:0",
"anthropic.claude-sonnet-4-20250514-v1:0",
"anthropic.claude-3-7-sonnet-20250219-v1:0",
"anthropic.claude-3-5-sonnet-20241022-v2:0",
"anthropic.claude-3-5-haiku-20241022-v1:0",
"anthropic.claude-3-opus-20240229-v1:0",
"anthropic.claude-3-sonnet-20240229-v1:0",
"anthropic.claude-3-haiku-20240307-v1:0",
// Amazon Nova
"amazon.nova-2-lite-v1:0",
"amazon.nova-premier-v1:0",
"amazon.nova-pro-v1:0",
"amazon.nova-lite-v1:0",
"amazon.nova-micro-v1:0",
// Meta Llama
"meta.llama4-maverick-17b-instruct-v1:0",
"meta.llama4-scout-17b-instruct-v1:0",
"meta.llama3-3-70b-instruct-v1:0",
"meta.llama3-1-405b-instruct-v1:0",
"meta.llama3-1-70b-instruct-v1:0",
// Mistral
"mistral.mistral-large-2411-v1:0",
"mistral.mistral-small-2503-v1:0",
"mistral.mistral-large-3-675b-instruct",
"mistral.pixtral-large-2502-v1:0",
],
openrouter: [
// Anthropic
"anthropic/claude-sonnet-4",
"anthropic/claude-opus-4",
"anthropic/claude-3.5-sonnet",
"anthropic/claude-3.5-haiku",
"anthropic/claude-opus-4.8",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5",
// OpenAI
"openai/gpt-4o",
"openai/gpt-5.5",
"openai/gpt-5.4",
"openai/gpt-5.4-mini",
"openai/gpt-4o-mini",
"openai/o1",
"openai/o3-mini",
// Google
"google/gemini-2.5-pro",
"google/gemini-2.5-flash",
"google/gemini-2.0-flash-exp:free",
"google/gemini-3.1-pro-preview",
"google/gemini-3.5-flash",
"google/gemini-2.5-flash-lite",
// xAI
"x-ai/grok-4.3",
// Meta Llama
"meta-llama/llama-4-maverick",
"meta-llama/llama-4-scout",
"meta-llama/llama-3.3-70b-instruct",
"meta-llama/llama-3.1-405b-instruct",
"meta-llama/llama-3.1-70b-instruct",
// DeepSeek
"deepseek/deepseek-chat",
"deepseek/deepseek-r1",
"deepseek/deepseek-v4-pro",
"deepseek/deepseek-v3.2",
// Qwen
"qwen/qwen-2.5-72b-instruct",
"qwen/qwen3.7-max",
"qwen/qwen3-coder",
// MiniMax
"minimax/minimax-m3",
],
deepseek: [
"deepseek-v4-pro",
"deepseek-v4-flash",
"deepseek-chat",
"deepseek-reasoner",
],
deepseek: ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"],
siliconflow: [
// DeepSeek
"deepseek-ai/DeepSeek-V3",
"deepseek-ai/DeepSeek-R1",
"deepseek-ai/DeepSeek-V2.5",
"deepseek-ai/DeepSeek-V4-Pro",
"deepseek-ai/DeepSeek-V4-Flash",
"deepseek-ai/DeepSeek-V3.2",
// MiniMax
"MiniMaxAI/MiniMax-M3",
// Moonshot
"moonshotai/Kimi-K2.6",
// Z.ai
"zai-org/GLM-5",
// Qwen
"Qwen/Qwen2.5-72B-Instruct",
"Qwen/Qwen2.5-32B-Instruct",
"Qwen/Qwen2.5-Coder-32B-Instruct",
"Qwen/Qwen2.5-7B-Instruct",
"Qwen/Qwen2-VL-72B-Instruct",
"qwen3.5-plus",
"Qwen/Qwen3.6-35B-A3B",
"Qwen/Qwen3-Coder-480B-A35B-Instruct",
"Qwen/Qwen3-30B-A3B-Instruct-2507",
"Qwen/Qwen3-VL-32B-Instruct",
// OpenAI open-weights
"openai/gpt-oss-120b",
],
sglang: [
// SGLang is OpenAI-compatible, models depend on deployment
"default",
],
gateway: [
"openai/gpt-4o",
"openai/gpt-4o-mini",
"anthropic/claude-sonnet-4-5",
"anthropic/claude-3-5-sonnet",
"google/gemini-2.0-flash",
"openai/gpt-5.5",
"anthropic/claude-opus-4.7",
"google/gemini-3.1-pro-preview",
"xai/grok-4.3",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5",
"openai/gpt-5.4-mini",
],
edgeone: ["@tx/deepseek-ai/deepseek-v32"],
doubao: [
// ByteDance Doubao models
"doubao-1.5-thinking-pro-250415",
"doubao-1.5-thinking-pro-m-250428",
"doubao-1.5-pro-32k-250115",
"doubao-1.5-pro-256k-250115",
"doubao-pro-32k-241215",
"doubao-pro-256k-241215",
// ByteDance Doubao models (Volcengine Ark IDs use dash form)
"doubao-seed-2-0-pro-260215",
"doubao-seed-2-0-lite-260428",
"doubao-seed-2-0-mini-260428",
"doubao-seed-1-8-251228",
"doubao-seed-1-6-251015",
"doubao-seed-1-6-flash-250828",
"doubao-seed-1-6-vision-250815",
"doubao-1-5-pro-32k-250115",
"doubao-1-5-lite-32k-250115",
],
modelscope: [
// DeepSeek
"deepseek-ai/DeepSeek-V4-Pro",
"deepseek-ai/DeepSeek-V3.2",
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-R1",
// Qwen
"Qwen/Qwen2.5-72B-Instruct",
"Qwen/Qwen2.5-32B-Instruct",
"Qwen/Qwen3-235B-A22B-Instruct-2507",
"Qwen/Qwen3-VL-235B-A22B-Instruct",
"Qwen/Qwen3-Coder-30B-A3B-Instruct",
"Qwen/Qwen3-32B",
"qwen3.5-plus",
// DeepSeek
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-V3.2",
"Qwen/Qwen2.5-72B-Instruct",
],
minimax: [
// MiniMax models (Anthropic-compatible API)
"MiniMax-M3",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
],
novita: [
// Novita AI models (OpenAI-compatible API)
"moonshotai/kimi-k2.5",
"zai-org/glm-5",
"minimax/minimax-m2.5",
"minimax/minimax-m3",
"deepseek/deepseek-v4-pro",
"zai-org/glm-5.1",
"moonshotai/kimi-k2.6",
"deepseek/deepseek-v4-flash",
],
}

View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest"
import { isPrivateUrl } from "@/lib/ssrf-protection"
describe("isPrivateUrl", () => {
it("blocks private IPv6 URLs", () => {
expect(isPrivateUrl("http://[::1]/")).toBe(true)
expect(isPrivateUrl("http://[0:0:0:0:0:0:0:1]/")).toBe(true)
expect(isPrivateUrl("http://[::]/")).toBe(true)
expect(isPrivateUrl("http://[::ffff:127.0.0.1]/")).toBe(true)
expect(isPrivateUrl("http://[fc00::1]/")).toBe(true)
expect(isPrivateUrl("http://[fd12:3456:789a::1]/")).toBe(true)
expect(isPrivateUrl("http://[fe80::1]/")).toBe(true)
expect(isPrivateUrl("http://[fe9f::1]/")).toBe(true)
expect(isPrivateUrl("http://[febf::1]/")).toBe(true)
})
it("allows public URLs", () => {
expect(isPrivateUrl("https://example.com/article")).toBe(false)
expect(isPrivateUrl("https://fc00.example.com/article")).toBe(false)
})
})