From 40cea6434ab4c263fea05cbfa6849f9c7ff11979 Mon Sep 17 00:00:00 2001 From: NeverlandYao <865373013@qq.com> Date: Thu, 4 Sep 2025 10:28:30 +0800 Subject: [PATCH] feat: Add Jina AI service integration and web fragment input functionality - Add Jina AI service for web content extraction and summarization - Add web fragment input component - Update layout and fragment input components - Add MongoDB configuration - Add example documentation --- .gitignore | 4 + app/api/jina/extract/route.ts | 113 ++++++++ app/layout.tsx | 1 + components/fragment-input.tsx | 60 +++- components/web-fragment-input.tsx | 465 ++++++++++++++++++++++++++++++ examples/mongodb-usage.md | 93 ++++++ lib/jina-service.ts | 385 +++++++++++++++++++++++++ lib/mongodb-config.ts | 64 ++++ 8 files changed, 1171 insertions(+), 14 deletions(-) create mode 100644 app/api/jina/extract/route.ts create mode 100644 components/web-fragment-input.tsx create mode 100644 examples/mongodb-usage.md create mode 100644 lib/jina-service.ts create mode 100644 lib/mongodb-config.ts diff --git a/.gitignore b/.gitignore index e72b4d6..c021640 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,10 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env +.env.local +.env.development.local +.env.test.local +.env.production.local # vercel .vercel diff --git a/app/api/jina/extract/route.ts b/app/api/jina/extract/route.ts new file mode 100644 index 0000000..0ac50a3 --- /dev/null +++ b/app/api/jina/extract/route.ts @@ -0,0 +1,113 @@ +/** + * Jina AI 网页内容提取 API 路由 + */ + +import { NextRequest, NextResponse } from 'next/server' +import { jinaService } from '../../../../lib/jina-service' +import { WebContentExtractRequest } from '../../../../lib/jina-service' + +export async function POST(request: NextRequest) { + try { + const body = await request.json() + const { url, userPrompt, extractMode = 'full', language = 'zh' } = body + + // 验证必需参数 + if (!url) { + return NextResponse.json( + { success: false, error: '缺少必需参数: url' }, + { status: 400 } + ) + } + + // 验证URL格式 + try { + new URL(url) + } catch { + return NextResponse.json( + { success: false, error: '无效的URL格式' }, + { status: 400 } + ) + } + + // 构建请求参数 + const extractRequest: WebContentExtractRequest = { + url, + userPrompt, + extractMode, + language + } + + // 调用Jina服务提取内容 + const result = await jinaService.extractWebContent(extractRequest) + + if (!result) { + return NextResponse.json( + { success: false, error: '网页内容提取失败' }, + { status: 500 } + ) + } + + return NextResponse.json({ + success: true, + data: result + }) + + } catch (error) { + console.error('Jina extract API error:', error) + return NextResponse.json( + { success: false, error: '服务器内部错误' }, + { status: 500 } + ) + } +} + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const action = searchParams.get('action') + + if (action === 'test') { + // 测试Jina服务连接 + const testUrl = 'https://example.com' + const testResult = await jinaService.parseWebPage(testUrl) + + return NextResponse.json({ + success: true, + data: { + service: 'Jina AI', + status: testResult.success ? 'connected' : 'error', + error: testResult.error + } + }) + } + + if (action === 'config') { + // 返回服务配置信息 + return NextResponse.json({ + success: true, + data: { + readerApiEnabled: true, + deepSearchEnabled: !!process.env.JINA_API_KEY, + supportedModes: ['full', 'summary', 'custom'], + maxContentLength: 8000, + rateLimits: { + reader: '20 requests/minute (free), 500 requests/minute (paid)', + deepSearch: '50 requests/minute' + } + } + }) + } + + return NextResponse.json( + { success: false, error: '无效的操作' }, + { status: 400 } + ) + + } catch (error) { + console.error('Jina config API error:', error) + return NextResponse.json( + { success: false, error: '获取配置失败' }, + { status: 500 } + ) + } +} diff --git a/app/layout.tsx b/app/layout.tsx index e0b9429..8474dda 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -29,6 +29,7 @@ export default function RootLayout({ diff --git a/components/fragment-input.tsx b/components/fragment-input.tsx index ad548c1..15dbbef 100644 --- a/components/fragment-input.tsx +++ b/components/fragment-input.tsx @@ -3,8 +3,12 @@ import { useState } from "react" import { Textarea } from "@/components/ui/textarea" import { Button } from "@/components/ui/button" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { FileText, Globe } from "lucide-react" // API调用现在通过Next.js API路由处理,不再需要直接导入 import { useFragmentStore } from "@/lib/stores/fragment-store" +import { WebFragmentInput } from "./web-fragment-input" export function FragmentInput() { const [text, setText] = useState("") @@ -127,20 +131,48 @@ export function FragmentInput() { } return ( -
-