use url_preview::{
LLMExtractor, LLMExtractorConfig, LLMProvider,
PreviewError, ContentFormat, Fetcher,
};
use serde::{Deserialize, Serialize};
use schemars::JsonSchema;
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;
struct MockClaudeCodeProvider {
system_prompt: String,
model: String,
}
impl MockClaudeCodeProvider {
fn new() -> Self {
Self {
system_prompt: "You are an expert at extracting structured data from web content.".to_string(),
model: "claude-3-opus-20240229".to_string(),
}
}
fn with_sonnet(mut self) -> Self {
self.model = "claude-3-sonnet-20240229".to_string();
self
}
}
#[async_trait]
impl LLMProvider for MockClaudeCodeProvider {
fn name(&self) -> &str {
"claude-code-mock"
}
async fn generate(
&self,
_prompt: String,
schema: Value,
_config: &LLMExtractorConfig,
) -> Result<Value, PreviewError> {
if let Some(properties) = schema.get("properties") {
let mut result = serde_json::Map::new();
if let Some(props) = properties.as_object() {
for (key, _) in props {
match key.as_str() {
"title" => result.insert(key.clone(), Value::String("Rust Programming Language".to_string())),
"description" => result.insert(key.clone(), Value::String("A language empowering everyone to build reliable and efficient software.".to_string())),
"topics" => result.insert(key.clone(), Value::Array(vec![
Value::String("systems programming".to_string()),
Value::String("memory safety".to_string()),
Value::String("concurrency".to_string()),
])),
"key_features" => result.insert(key.clone(), Value::Array(vec![
Value::String("Zero-cost abstractions".to_string()),
Value::String("Memory safety without garbage collection".to_string()),
Value::String("Concurrency without data races".to_string()),
])),
_ => result.insert(key.clone(), Value::String(format!("Mock value for {}", key))),
};
}
}
Ok(Value::Object(result))
} else {
Ok(Value::Object(serde_json::Map::new()))
}
}
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct WebPageAnalysis {
title: String,
description: String,
topics: Vec<String>,
key_features: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct ProductComparison {
product_name: String,
competitors: Vec<String>,
unique_features: Vec<String>,
market_position: String,
}
async fn demonstrate_basic_usage() -> Result<(), PreviewError> {
println!("\n📝 Basic Usage Example");
println!("{}", "=".repeat(50));
let provider = Arc::new(MockClaudeCodeProvider::new());
let fetcher = Fetcher::new();
let extractor = LLMExtractor::new(provider);
let url = "https://www.rust-lang.org/";
let result = extractor.extract::<WebPageAnalysis>(url, &fetcher).await?;
println!("URL: {}", url);
println!("\nExtracted Data:");
println!("Title: {}", result.data.title);
println!("Description: {}", result.data.description);
println!("\nTopics:");
for topic in &result.data.topics {
println!(" • {}", topic);
}
println!("\nKey Features:");
for feature in &result.data.key_features {
println!(" ✓ {}", feature);
}
Ok(())
}
async fn demonstrate_custom_config() -> Result<(), PreviewError> {
println!("\n\n⚙️ Custom Configuration Example");
println!("{}", "=".repeat(50));
let provider = Arc::new(MockClaudeCodeProvider::new().with_sonnet());
let config = LLMExtractorConfig {
format: ContentFormat::Markdown,
clean_html: true,
max_content_length: 5000,
model_params: Default::default(),
};
let fetcher = Fetcher::new();
let extractor = LLMExtractor::with_config(provider, config);
let url = "https://crates.io/";
let result = extractor.extract::<ProductComparison>(url, &fetcher).await?;
println!("URL: {}", url);
println!("\nProduct Analysis:");
println!("Product: {}", result.data.product_name);
println!("Market Position: {}", result.data.market_position);
Ok(())
}
async fn demonstrate_concurrent_extraction() -> Result<(), PreviewError> {
println!("\n\n🚀 Concurrent Extraction Example");
println!("{}", "=".repeat(50));
let provider = Arc::new(MockClaudeCodeProvider::new());
let fetcher = Arc::new(Fetcher::new());
let extractor = Arc::new(LLMExtractor::new(provider));
let urls = vec![
"https://www.rust-lang.org/",
"https://doc.rust-lang.org/",
"https://crates.io/",
];
let mut handles = vec![];
for url in urls {
let extractor = Arc::clone(&extractor);
let fetcher = Arc::clone(&fetcher);
let url_string = url.to_string();
let url_clone = url_string.clone();
let handle = tokio::spawn(async move {
extractor.extract::<WebPageAnalysis>(&url_string, &*fetcher).await
});
handles.push((url_clone, handle));
}
println!("Extracting from multiple URLs concurrently...\n");
for (url, handle) in handles {
match handle.await {
Ok(Ok(result)) => {
println!("✅ {} - {}", url, result.data.title);
}
Ok(Err(e)) => {
println!("❌ {} - Error: {}", url, e);
}
Err(e) => {
println!("❌ {} - Task error: {}", url, e);
}
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🤖 Claude Code SDK Integration Pattern Demo");
println!("{}", "=".repeat(50));
println!("This demonstrates how the claude-code-sdk would integrate");
println!("with url-preview once the SDK compilation issue is fixed.\n");
demonstrate_basic_usage().await?;
demonstrate_custom_config().await?;
demonstrate_concurrent_extraction().await?;
println!("\n\n📋 Integration Steps:");
println!("1. Fix the claude-code-sdk compilation issue");
println!("2. Add to Cargo.toml:");
println!(" claude-code-sdk = {{ path = \"../rust-claude-code-api/claude-code-sdk-rs\" }}");
println!("3. Use ClaudeCodeProvider from url_preview:");
println!(" use url_preview::{{LLMExtractor, ClaudeCodeProvider}};");
println!(" let provider = ClaudeCodeProvider::new();");
println!(" let extractor = LLMExtractor::with_provider(Box::new(provider));");
println!("\n🎯 The actual ClaudeCodeProvider implementation is in:");
println!(" src/llm_providers/claude_code.rs");
Ok(())
}