गो में ओल्लामा वेब सर्च एपीआई का उपयोग
Go और Ollama के साथ AI खोज एजेंट्स बनाएं
Ollama के वेब सर्च API आपको वास्तविक समय के वेब जानकारी के साथ स्थानीय LLMs को बढ़ाने की अनुमति देता है। यह गाइड आपको Go में वेब सर्च क्षमताओं को लागू करने का तरीका दिखाता है, सरल API कॉल्स से लेकर पूर्ण-फीचर सर्च एजेंट्स तक।

शुरू करने के लिए
क्या Ollama के पास वेब सर्च के लिए एक आधिकारिक Go लाइब्रेरी है? Ollama एक REST API प्रदान करता है जो किसी भी Go HTTP क्लाइंट के साथ काम करता है। हालाँकि अभी तक वेब सर्च के लिए कोई आधिकारिक Go SDK नहीं है, लेकिन आप स्टैंडर्ड लाइब्रेरी पैकेजों का उपयोग करके API कॉल्स को आसानी से लागू कर सकते हैं।
पहले, अपने Ollama खाते से एक API की बनाएँ। Ollama कमांड्स और उपयोग के बारे में व्यापक संदर्भ के लिए, Ollama चिट्ठा देखें।
अपना API की एक पर्यावरण चर के रूप में सेट करें:
export OLLAMA_API_KEY="your_api_key"
Windows PowerShell पर:
$env:OLLAMA_API_KEY = "your_api_key"
प्रोजेक्ट सेटअप
एक नया Go मॉड्यूल बनाएँ:
mkdir ollama-search
cd ollama-search
go mod init ollama-search
बेसिक वेब सर्च
Go में Ollama के वेब सर्च API के साथ कैसे प्रमाणित करें? Authorization हेडर को अपने API की को एक Bearer टोकन के रूप में सेट करें। अपने Ollama खाते से एक API की बनाएँ और इसे रिक्वेस्ट हेडर में पास करें।
यह वेब सर्च API का पूर्ण कार्यान्वयन है:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Request/Response types for web_search
type WebSearchRequest struct {
Query string `json:"query"`
MaxResults int `json:"max_results,omitempty"`
}
type WebSearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
}
type WebSearchResponse struct {
Results []WebSearchResult `json:"results"`
}
func webSearch(query string, maxResults int) (*WebSearchResponse, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY environment variable not set")
}
reqBody := WebSearchRequest{
Query: query,
MaxResults: maxResults,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var searchResp WebSearchResponse
if err := json.Unmarshal(body, &searchResp); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &searchResp, nil
}
func main() {
results, err := webSearch("What is Ollama?", 5)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Search Results:")
fmt.Println("===============")
for i, result := range results.Results {
fmt.Printf("\n%d. %s\n", i+1, result.Title)
fmt.Printf(" URL: %s\n", result.URL)
fmt.Printf(" %s\n", truncate(result.Content, 150))
}
}
func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
वेब फेच कार्यान्वयन
web_search और web_fetch एंडपॉइंट्स में क्या अंतर है? web_search एंडपॉइंट इंटरनेट को क्वेरी करता है और शीर्षकों, URL और स्निपेट्स के साथ कई सर्च रिजल्ट्स लौटाता है। web_fetch एंडपॉइंट एक विशिष्ट URL के पूर्ण सामग्री को प्राप्त करता है, पेज शीर्षक, सामग्री और लिंक्स लौटाता है।
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Request/Response types for web_fetch
type WebFetchRequest struct {
URL string `json:"url"`
}
type WebFetchResponse struct {
Title string `json:"title"`
Content string `json:"content"`
Links []string `json:"links"`
}
func webFetch(url string) (*WebFetchResponse, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY environment variable not set")
}
reqBody := WebFetchRequest{URL: url}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", "https://ollama.com/api/web_fetch", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var fetchResp WebFetchResponse
if err := json.Unmarshal(body, &fetchResp); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &fetchResp, nil
}
func main() {
result, err := webFetch("https://ollama.com")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Title: %s\n\n", result.Title)
fmt.Printf("Content:\n%s\n\n", result.Content)
fmt.Printf("Links found: %d\n", len(result.Links))
for i, link := range result.Links {
if i >= 5 {
fmt.Printf(" ... and %d more\n", len(result.Links)-5)
break
}
fmt.Printf(" - %s\n", link)
}
}
पुन: उपयोग योग्य क्लाइंट पैकेज
एक पुन: उपयोग योग्य Ollama क्लाइंट पैकेज बनाएँ ताकि कोड साफ हो:
// ollama/client.go
package ollama
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type Client struct {
apiKey string
httpClient *http.Client
baseURL string
}
func NewClient() (*Client, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY environment variable not set")
}
return &Client{
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
baseURL: "https://ollama.com/api",
}, nil
}
type SearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
}
type SearchResponse struct {
Results []SearchResult `json:"results"`
}
type FetchResponse struct {
Title string `json:"title"`
Content string `json:"content"`
Links []string `json:"links"`
}
func (c *Client) WebSearch(query string, maxResults int) (*SearchResponse, error) {
payload := map[string]interface{}{
"query": query,
"max_results": maxResults,
}
return doRequest[SearchResponse](c, "/web_search", payload)
}
func (c *Client) WebFetch(url string) (*FetchResponse, error) {
payload := map[string]string{"url": url}
return doRequest[FetchResponse](c, "/web_fetch", payload)
}
func doRequest[T any](c *Client, endpoint string, payload interface{}) (*T, error) {
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", c.baseURL+endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
var result T
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
उपयोग:
package main
import (
"fmt"
"log"
"ollama-search/ollama"
)
func main() {
client, err := ollama.NewClient()
if err != nil {
log.Fatal(err)
}
// Search
results, err := client.WebSearch("Ollama new features", 5)
if err != nil {
log.Fatal(err)
}
for _, r := range results.Results {
fmt.Printf("- %s\n %s\n\n", r.Title, r.URL)
}
// Fetch
page, err := client.WebFetch("https://ollama.com")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Page: %s\n", page.Title)
}
एक सर्च एजेंट बनाना
गो-आधारित ओल्लामा सर्च एजेंट्स के लिए कौन से मॉडल सबसे अच्छे काम करते हैं? टूल-यूज क्षमताओं वाले मॉडल सबसे अच्छे काम करते हैं, जिसमें qwen3, gpt-oss, और क्लाउड मॉडल जैसे qwen3:480b-cloud और deepseek-v3.1-cloud शामिल हैं। अगर आप Qwen3 मॉडल्स का उपयोग कर रहे हैं और सर्च रिजल्ट्स को प्रोसेस या रीरैंक करने की आवश्यकता है, तो हमारे गाइड पर देखें Ollama और Qwen3 Embedding model के साथ टेक्स्ट दस्तावेज़ों को रीरैंक करना - Go में।
यहाँ एक पूर्ण सर्च एजेंट इम्प्लीमेंटेशन है:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Chat types
type Message struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolName string `json:"tool_name,omitempty"`
}
type ToolCall struct {
Function FunctionCall `json:"function"`
}
type FunctionCall struct {
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments"`
}
type Tool struct {
Type string `json:"type"`
Function ToolFunction `json:"function"`
}
type ToolFunction struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters map[string]interface{} `json:"parameters"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
Stream bool `json:"stream"`
}
type ChatResponse struct {
Message Message `json:"message"`
}
// SearchAgent LLM के साथ वेब सर्च को ऑर्केस्ट्रेट करता है
type SearchAgent struct {
model string
ollamaURL string
apiKey string
maxIter int
httpClient *http.Client
}
func NewSearchAgent(model string) *SearchAgent {
return &SearchAgent{
model: model,
ollamaURL: "http://localhost:11434/api/chat",
apiKey: os.Getenv("OLLAMA_API_KEY"),
maxIter: 10,
httpClient: &http.Client{},
}
}
func (a *SearchAgent) Query(question string) (string, error) {
tools := []Tool{
{
Type: "function",
Function: ToolFunction{
Name: "web_search",
Description: "वर्तमान जानकारी के लिए वेब सर्च करें",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"query": map[string]string{
"type": "string",
"description": "सर्च क्वेरी",
},
},
"required": []string{"query"},
},
},
},
{
Type: "function",
Function: ToolFunction{
Name: "web_fetch",
Description: "एक वेब पेज के पूर्ण सामग्री को प्राप्त करें",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"url": map[string]string{
"type": "string",
"description": "प्राप्त करने के लिए URL",
},
},
"required": []string{"url"},
},
},
},
}
messages := []Message{
{Role: "user", Content: question},
}
for i := 0; i < a.maxIter; i++ {
response, err := a.chat(messages, tools)
if err != nil {
return "", fmt.Errorf("चैट त्रुटि: %w", err)
}
messages = append(messages, response.Message)
// कोई टूल कॉल्स नहीं होने का मतलब है कि हमारा अंतिम उत्तर है
if len(response.Message.ToolCalls) == 0 {
return response.Message.Content, nil
}
// टूल कॉल्स को एक्सीक्यूट करें
for _, toolCall := range response.Message.ToolCalls {
fmt.Printf("🔧 कॉलिंग: %s\n", toolCall.Function.Name)
result, err := a.executeTool(toolCall)
if err != nil {
result = fmt.Sprintf("त्रुटि: %v", err)
}
// कॉन्टेक्स्ट लिमिट्स के लिए ट्रंकेट करें
if len(result) > 8000 {
result = result[:8000] + "... [ट्रंकेटेड]"
}
messages = append(messages, Message{
Role: "tool",
Content: result,
ToolName: toolCall.Function.Name,
})
}
}
return "", fmt.Errorf("अधिकतम इटरेशन पहुंच गए")
}
func (a *SearchAgent) chat(messages []Message, tools []Tool) (*ChatResponse, error) {
reqBody := ChatRequest{
Model: a.model,
Messages: messages,
Tools: tools,
Stream: false,
}
jsonData, _ := json.Marshal(reqBody)
resp, err := a.httpClient.Post(a.ollamaURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var chatResp ChatResponse
if err := json.Unmarshal(body, &chatResp); err != nil {
return nil, err
}
return &chatResp, nil
}
func (a *SearchAgent) executeTool(toolCall ToolCall) (string, error) {
switch toolCall.Function.Name {
case "web_search":
query, ok := toolCall.Function.Arguments["query"].(string)
if !ok {
return "", fmt.Errorf("अवैध क्वेरी आर्ग्युमेंट")
}
return a.webSearch(query)
case "web_fetch":
url, ok := toolCall.Function.Arguments["url"].(string)
if !ok {
return "", fmt.Errorf("अवैध URL आर्ग्युमेंट")
}
return a.webFetch(url)
default:
return "", fmt.Errorf("अज्ञात टूल: %s", toolCall.Function.Name)
}
}
func (a *SearchAgent) webSearch(query string) (string, error) {
payload := map[string]interface{}{"query": query, "max_results": 5}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+a.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := a.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return string(body), nil
}
func (a *SearchAgent) webFetch(url string) (string, error) {
payload := map[string]string{"url": url}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_fetch", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+a.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := a.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return string(body), nil
}
func main() {
agent := NewSearchAgent("qwen3:4b")
answer, err := agent.Query("Ollama में सबसे नए फीचर्स क्या हैं?")
if err != nil {
fmt.Printf("त्रुटि: %v\n", err)
return
}
fmt.Println("\n📝 उत्तर:")
fmt.Println(answer)
}
गो में बड़े वेब सर्च रिस्पॉन्स कैसे हैंडल करें? मॉडल कॉन्टेक्स्ट को पास करने से पहले रिस्पॉन्स कंटेंट को ट्रंकेट करें। कॉन्टेक्स्ट लिमिट्स में फिट होने के लिए कंटेंट को लगभग 8000 अक्षरों तक सीमित करने के लिए स्ट्रिंग स्लाइसिंग का उपयोग करें।
समकालिक सर्च
गो समकालिक ऑपरेशन्स में उत्कृष्ट है। यहाँ कई सर्चों को समानांतर में करने का तरीका है। यह समझने से आप अपने समकालिक सर्च इम्प्लीमेंटेशन्स को ऑप्टिमाइज़ कर सकते हैं कि Ollama समकालिक रिक्वेस्ट्स को कैसे हैंडल करता है।
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"sync"
)
type SearchResult struct {
Query string
Results []struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
} `json:"results"`
Error error
}
func concurrentSearch(queries []string) []SearchResult {
results := make([]SearchResult, len(queries))
var wg sync.WaitGroup
for i, query := range queries {
wg.Add(1)
go func(idx int, q string) {
defer wg.Done()
result := SearchResult{Query: q}
payload := map[string]string{"query": q}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OLLAMA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
result.Error = err
results[idx] = result
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &result)
results[idx] = result
}(i, query)
}
wg.Wait()
return results
}
func main() {
queries := []string{
"Ollama के सबसे नए फीचर्स",
"लोकल LLM डिप्लॉयमेंट",
"AI सर्च एजेंट्स Go",
}
results := concurrentSearch(queries)
for _, r := range results {
fmt.Printf("\n🔍 क्वेरी: %s\n", r.Query)
if r.Error != nil {
fmt.Printf(" त्रुटि: %v\n", r.Error)
continue
}
for _, item := range r.Results[:min(3, len(r.Results))] {
fmt.Printf(" • %s\n", item.Title)
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
कॉन्टेक्स्ट और कैंसलेशन
टाइमआउट और कैंसलेशन के लिए कॉन्टेक्स्ट सपोर्ट जोड़ें:
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
func webSearchWithContext(ctx context.Context, query string) (string, error) {
payload := map[string]string{"query": query}
jsonData, _ := json.Marshal(payload)
req, err := http.NewRequestWithContext(ctx, "POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("OLLAMA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
func main() {
// 10 सेकंड टाइमआउट के साथ कॉन्टेक्स्ट बनाएं
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := webSearchWithContext(ctx, "Ollama वेब सर्च API")
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("रिक्वेस्ट टाइमआउट हो गया")
} else {
fmt.Printf("त्रुटि: %v\n", err)
}
return
}
fmt.Println(result)
}
अनुशंसित मॉडल्स
गो सर्च एजेंट्स के लिए मुझे किस कॉन्टेक्स्ट लेंथ का उपयोग करना चाहिए? अच्छे प्रदर्शन के लिए कॉन्टेक्स्ट लेंथ को लगभग 32000 टोकन्स पर सेट करें। सर्च एजेंट्स वेब सर्च रिजल्ट्स को हैंडल करने के लिए पूर्ण कॉन्टेक्स्ट लेंथ के साथ सबसे अच्छे काम करते हैं। अगर आपको अपने ओल्लामा मॉडल्स को मैनेज करना है या उन्हें अलग-अलग स्थानों पर मूव करना है, तो हमारे गाइड पर देखें Ollama मॉडल्स को अलग ड्राइव या फोल्डर में मूव कैसे करें।
| मॉडल | पैरामीटर्स | सबसे अच्छा उपयोग |
|---|---|---|
qwen3:4b |
4B | तेज़ लोकल सर्च |
qwen3 |
8B | सामान्य उद्देश्य एजेंट |
gpt-oss |
विभिन्न | रिसर्च टास्क |
qwen3:480b-cloud |
480B | जटिल रीज़निंग (क्लाउड) |
gpt-oss:120b-cloud |
120B | लंबे रूप में रिसर्च (क्लाउड) |
deepseek-v3.1-cloud |
- | उन्नत विश्लेषण (क्लाउड) |
टेक्स्ट और विज़ुअल कंटेंट को मिलाने वाले उन्नत AI एप्लिकेशन्स के लिए, अपने सर्च क्षमताओं को टेक्स्ट-ओनली क्वेरी से परे बढ़ाने के लिए क्रॉस-मोडल एम्बेडिंग्स का पता लगाने का विचार करें।
सर्वोत्तम प्रथाएँ
- त्रुटि प्रबंधन: हमेशा त्रुटियों की जांच करें और API विफलताओं को सुशोभित रूप से संभालें
- समय सीमा: नेटवर्क अनुरोधों के लिए समय सीमा के साथ संदर्भ का उपयोग करें
- दर सीमा: Ollama के API दर सीमाओं का सम्मान करें। Ollama के API में संभावित परिवर्तनों के बारे में जागरूक रहें, जैसा कि Ollama के Enshittification के पहले संकेत में चर्चा की गई है
- परिणाम कटौती: संदर्भ सीमाओं के लिए परिणामों को ~8000 चर तक काटें
- समानांतर अनुरोध: समानांतर खोजों के लिए goroutines का उपयोग करें
- कनेक्शन पुनः उपयोग: बेहतर प्रदर्शन के लिए HTTP क्लाइंट को पुनः उपयोग करें
- परीक्षण: अपने खोज कार्यान्वयनों के लिए व्यापक इकाई परीक्षण लिखें। Go इकाई परीक्षण सर्वोत्तम प्रथाओं का पालन करें ताकि आपका कोड मजबूत और बनाए रखने योग्य हो
उपयोगी लिंक
- Ollama चिट्ठा
- Ollama और Qwen3 Embedding मॉडल के साथ पाठ दस्तावेजों को रीरैंकिंग - Go में
- क्रॉस-मोडल एम्बेडिंग्स: AI मोडलिटीज़ को जोड़ना
- Go इकाई परीक्षण: संरचना और सर्वोत्तम प्रथाएँ
- Ollama मॉडल्स को अलग ड्राइव या फोल्डर में कैसे मूव करें
- Ollama समानांतर अनुरोधों को कैसे हैंडल करता है
- Ollama Enshittification के पहले संकेत
- Ollama वेब सर्च ब्लॉग पोस्ट
- Ollama आधिकारिक दस्तावेज़ीकरण
- Ollama Go उदाहरण
- Ollama GitHub रिपॉजिटरी