- Published on
Memahami Ollama dan Deepseek: Integrasi dengan React
Ollama
Gambaran Umum
Ollama adalah tool open-source yang memungkinkan pengguna menjalankan large language models (LLMs) secara lokal. Panduan ini fokus pada integrasi Ollama dengan aplikasi React.
Integrasi Dasar React
// OllamaClient.tsx
import React, { useState } from 'react'
import axios from 'axios'
interface OllamaResponse {
response: string
context: number[]
}
const OllamaClient: React.FC = () => {
const [prompt, setPrompt] = useState('')
const [response, setResponse] = useState('')
const [loading, setLoading] = useState(false)
const queryOllama = async () => {
setLoading(true)
try {
const result = await axios.post('http://localhost:11434/api/generate', {
model: 'llama2',
prompt: prompt,
})
setResponse(result.data.response)
} catch (error) {
console.error('Error:', error)
} finally {
setLoading(false)
}
}
return (
<div className="p-4">
<textarea
className="w-full rounded border p-2"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Masukkan prompt Anda..."
/>
<button
className="mt-2 rounded bg-blue-500 px-4 py-2 text-white"
onClick={queryOllama}
disabled={loading}
>
{loading ? 'Memproses...' : 'Generate'}
</button>
{response && (
<div className="mt-4 rounded border p-4">
<pre>{response}</pre>
</div>
)}
</div>
)
}
Custom Hook untuk Ollama
// useOllama.ts
import { useState, useCallback } from 'react'
import axios from 'axios'
interface OllamaConfig {
model?: string
baseUrl?: string
}
export const useOllama = (config: OllamaConfig = {}) => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const generate = useCallback(
async (prompt: string) => {
setLoading(true)
setError(null)
try {
const response = await axios.post(
`${config.baseUrl || 'http://localhost:11434'}/api/generate`,
{
model: config.model || 'llama2',
prompt,
}
)
return response.data.response
} catch (err) {
setError(err instanceof Error ? err.message : 'Terjadi kesalahan')
return null
} finally {
setLoading(false)
}
},
[config]
)
return { generate, loading, error }
}
// Contoh Penggunaan
const AIChat: React.FC = () => {
const { generate, loading, error } = useOllama({
model: 'codellama',
baseUrl: 'http://localhost:11434',
})
const handleGenerate = async () => {
const response = await generate('Tulis komponen React')
console.log(response)
}
}
Komponen Manajemen Model
// OllamaModels.tsx
import React, { useEffect, useState } from 'react'
import axios from 'axios'
interface Model {
name: string
size: number
modified: string
}
const OllamaModels: React.FC = () => {
const [models, setModels] = useState<Model[]>([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const fetchModels = async () => {
try {
const response = await axios.get('http://localhost:11434/api/tags')
setModels(response.data.models)
} catch (error) {
console.error('Error mengambil model:', error)
} finally {
setLoading(false)
}
}
fetchModels()
}, [])
return (
<div className="p-4">
<h2 className="mb-4 text-xl font-bold">Model yang Terinstal</h2>
{loading ? (
<div>Memuat model...</div>
) : (
<div className="grid gap-4">
{models.map((model) => (
<div key={model.name} className="rounded border p-4">
<h3 className="font-bold">{model.name}</h3>
<p>Ukuran: {(model.size / 1024 / 1024 / 1024).toFixed(2)} GB</p>
<p>Dimodifikasi: {new Date(model.modified).toLocaleDateString()}</p>
</div>
))}
</div>
)}
</div>
)
}
Deepseek
Integrasi React
// DeepseekClient.tsx
import React, { useState } from 'react'
import axios from 'axios'
interface DeepseekProps {
apiKey: string
model?: string
}
const DeepseekClient: React.FC<DeepseekProps> = ({ apiKey, model = 'deepseek-coder' }) => {
const [prompt, setPrompt] = useState('')
const [response, setResponse] = useState('')
const [loading, setLoading] = useState(false)
const generateResponse = async () => {
setLoading(true)
try {
const result = await axios.post(
'https://api.deepseek.com/v1/completions',
{
model,
prompt,
max_tokens: 500,
},
{
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
}
)
setResponse(result.data.choices[0].text)
} catch (error) {
console.error('Error:', error)
} finally {
setLoading(false)
}
}
return (
<div className="p-4">
<textarea
className="w-full rounded border p-2"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Masukkan prompt Anda..."
/>
<button
className="mt-2 rounded bg-blue-500 px-4 py-2 text-white"
onClick={generateResponse}
disabled={loading}
>
{loading ? 'Menghasilkan...' : 'Generate'}
</button>
{response && (
<div className="mt-4 rounded border bg-gray-50 p-4">
<pre className="whitespace-pre-wrap">{response}</pre>
</div>
)}
</div>
)
}
Custom Hook untuk Deepseek
// useDeepseek.ts
import { useState, useCallback } from 'react'
import axios from 'axios'
interface DeepseekConfig {
apiKey: string
model?: string
maxTokens?: number
}
export const useDeepseek = (config: DeepseekConfig) => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const generate = useCallback(
async (prompt: string) => {
setLoading(true)
setError(null)
try {
const response = await axios.post(
'https://api.deepseek.com/v1/completions',
{
model: config.model || 'deepseek-coder',
prompt,
max_tokens: config.maxTokens || 500,
},
{
headers: {
Authorization: `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
}
)
return response.data.choices[0].text
} catch (err) {
setError(err instanceof Error ? err.message : 'Terjadi kesalahan')
return null
} finally {
setLoading(false)
}
},
[config]
)
return { generate, loading, error }
}
Komponen Code Generator
// CodeGenerator.tsx
import React, { useState } from 'react'
import { useDeepseek } from './useDeepseek'
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'
interface CodeGeneratorProps {
apiKey: string
}
const CodeGenerator: React.FC<CodeGeneratorProps> = ({ apiKey }) => {
const [prompt, setPrompt] = useState('')
const [code, setCode] = useState('')
const { generate, loading, error } = useDeepseek({
apiKey,
model: 'deepseek-coder',
maxTokens: 1000,
})
const handleGenerate = async () => {
const response = await generate(prompt)
if (response) {
setCode(response)
}
}
return (
<div className="p-4">
<div className="mb-4">
<label className="mb-2 block text-sm font-medium">
Deskripsikan kode yang ingin Anda hasilkan:
</label>
<textarea
className="w-full rounded border p-2"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
rows={4}
/>
</div>
<button
className="rounded bg-blue-500 px-4 py-2 text-white"
onClick={handleGenerate}
disabled={loading}
>
{loading ? 'Menghasilkan...' : 'Generate Kode'}
</button>
{error && (
<div className="mt-4 rounded border border-red-500 bg-red-50 p-4 text-red-700">{error}</div>
)}
{code && (
<div className="mt-4">
<SyntaxHighlighter language="typescript" style={docco}>
{code}
</SyntaxHighlighter>
</div>
)}
</div>
)
}
Perbandingan dan Best Practice
Context Provider Pattern
// AIContext.tsx
import React, { createContext, useContext, ReactNode } from 'react'
import { useOllama } from './useOllama'
import { useDeepseek } from './useDeepseek'
interface AIContextType {
ollama: ReturnType<typeof useOllama>
deepseek: ReturnType<typeof useDeepseek>
}
const AIContext = createContext<AIContextType | null>(null)
export const AIProvider: React.FC<{
children: ReactNode
deepseekApiKey: string
}> = ({ children, deepseekApiKey }) => {
const ollama = useOllama()
const deepseek = useDeepseek({ apiKey: deepseekApiKey })
return <AIContext.Provider value={{ ollama, deepseek }}>{children}</AIContext.Provider>
}
export const useAI = () => {
const context = useContext(AIContext)
if (!context) {
throw new Error('useAI harus digunakan dalam AIProvider')
}
return context
}
Sumber Daya
Dokumentasi Resmi
Sumber Integrasi React
- React Query - Untuk mengelola state API
- Axios - Untuk request API
- React Syntax Highlighter
Tools Pengembangan
Best Practice Keamanan
- Simpan API key dalam environment variable
- Implementasikan rate limiting
- Sanitasi input dan output
- Gunakan HTTPS untuk panggilan API
- Implementasikan error handling yang tepat