Files
agents_and_robots/tools/imdb/imdb.go
T
egutierrez f8fa7e7c7b feat: agregar tool imdb_search para buscar películas en IMDb
Implementa una nueva tool que permite buscar películas y series en IMDb
usando la API de OMDb. Retorna hasta 5 resultados con título, año, tipo,
poster URL e IMDb ID.

Cambios:
- tools/imdb/imdb.go: tool imdb_search con integración a OMDb API
- internal/config/schema.go: IMDbToolCfg con api_key, api_key_env y timeout
- agents/runtime.go: registro de tool en buildToolRegistry
- agents/asistente-2/config.yaml: habilitación de tool imdb
- .env.example: OMDB_API_KEY para configuración

La tool soporta parámetros:
- query (requerido): título de película/serie a buscar
- year (opcional): año para filtrar resultados

Configuración via api_key directa o variable de entorno OMDB_API_KEY.
API key gratuita disponible en http://www.omdbapi.com/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-08 23:04:05 +00:00

168 lines
5.0 KiB
Go

package imdb
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/enmanuel/agents/internal/config"
"github.com/enmanuel/agents/tools"
)
// SearchResult represents a single movie/series result from OMDb API.
type SearchResult struct {
Title string `json:"Title"`
Year string `json:"Year"`
ImdbID string `json:"imdbID"`
Type string `json:"Type"`
Poster string `json:"Poster"`
}
// SearchResponse represents the full response from OMDb search endpoint.
type SearchResponse struct {
Search []SearchResult `json:"Search"`
TotalResults string `json:"totalResults"`
Response string `json:"Response"`
Error string `json:"Error"`
}
// NewIMDbSearch creates an imdb_search tool that searches movies on IMDb via OMDb API.
// Returns up to 5 results with title, year, type, poster URL, and IMDb ID.
// Requires API key from http://www.omdbapi.com/
func NewIMDbSearch(cfg config.IMDbToolCfg) tools.Tool {
timeout := cfg.Timeout
if timeout == 0 {
timeout = 10 * time.Second
}
client := &http.Client{Timeout: timeout}
return tools.Tool{
Def: tools.Def{
Name: "imdb_search",
Description: "Search for movies or series on IMDb by title. Returns up to 5 results with title, year, type, poster image URL, and IMDb ID.",
Parameters: []tools.Param{
{Name: "query", Type: "string", Description: "The movie or series title to search for", Required: true},
{Name: "year", Type: "integer", Description: "Optional year to filter results (e.g., 2020)", Required: false},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
query := tools.GetString(args, "query")
if query == "" {
return tools.Result{Err: fmt.Errorf("imdb_search: query is required")}
}
// Get API key from config or env var
apiKey := cfg.APIKey
if apiKey == "" && cfg.APIKeyEnv != "" {
apiKey = getEnvVar(cfg.APIKeyEnv)
}
if apiKey == "" {
return tools.Result{Err: fmt.Errorf("imdb_search: API key not configured (set imdb.api_key or imdb.api_key_env in config)")}
}
// Build search URL
searchURL := buildSearchURL(apiKey, query, args)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
if err != nil {
return tools.Result{Err: fmt.Errorf("imdb_search: %w", err)}
}
resp, err := client.Do(req)
if err != nil {
return tools.Result{Err: fmt.Errorf("imdb_search: %w", err)}
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return tools.Result{Err: fmt.Errorf("imdb_search: HTTP %d", resp.StatusCode)}
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
if err != nil {
return tools.Result{Err: fmt.Errorf("imdb_search: read body: %w", err)}
}
var searchResp SearchResponse
if err := json.Unmarshal(body, &searchResp); err != nil {
return tools.Result{Err: fmt.Errorf("imdb_search: parse response: %w", err)}
}
if searchResp.Response == "False" {
return tools.Result{Output: fmt.Sprintf("No se encontraron resultados para '%s'. Error: %s", query, searchResp.Error)}
}
// Format results (limit to first 5)
output := formatResults(searchResp.Search, query)
return tools.Result{Output: output}
},
}
}
// buildSearchURL constructs the OMDb API search URL with query parameters.
func buildSearchURL(apiKey, query string, args map[string]any) string {
params := url.Values{}
params.Set("apikey", apiKey)
params.Set("s", query)
params.Set("type", "movie") // default to movies, could be made configurable
// Add year filter if provided
if year := tools.GetInt(args, "year"); year > 0 {
params.Set("y", fmt.Sprintf("%d", year))
}
return fmt.Sprintf("https://www.omdbapi.com/?%s", params.Encode())
}
// formatResults converts search results into a readable text format.
func formatResults(results []SearchResult, query string) string {
if len(results) == 0 {
return fmt.Sprintf("No se encontraron películas para '%s'", query)
}
var builder strings.Builder
builder.WriteString(fmt.Sprintf("🎬 Resultados de IMDb para '%s':\n\n", query))
// Limit to 5 results
limit := 5
if len(results) < limit {
limit = len(results)
}
for i := 0; i < limit; i++ {
r := results[i]
builder.WriteString(fmt.Sprintf("%d. **%s** (%s)\n", i+1, r.Title, r.Year))
builder.WriteString(fmt.Sprintf(" • Tipo: %s\n", r.Type))
builder.WriteString(fmt.Sprintf(" • IMDb ID: %s\n", r.ImdbID))
if r.Poster != "" && r.Poster != "N/A" {
builder.WriteString(fmt.Sprintf(" • Poster: %s\n", r.Poster))
} else {
builder.WriteString(" • Poster: No disponible\n")
}
builder.WriteString(fmt.Sprintf(" • Link: https://www.imdb.com/title/%s/\n", r.ImdbID))
if i < limit-1 {
builder.WriteString("\n")
}
}
if len(results) > 5 {
builder.WriteString(fmt.Sprintf("\n... y %d resultado(s) más", len(results)-5))
}
return builder.String()
}
// getEnvVar retrieves an environment variable by name.
func getEnvVar(name string) string {
return os.Getenv(name)
}