Skip to content

RustSandbox/Credify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Credify Logo

Credify

Crates.io Documentation CI License: MIT License: Apache 2.0

A robust Rust library for validating LinkedIn profile URLs with AI-first design. Built for the era of AI agents and LLMs, Credify provides both traditional validation APIs and specialized functions optimized for AI tool calling, especially with frameworks like Rig.

🎯 New in v0.4.0: Enhanced LinkedIn 404 detection with comprehensive apostrophe encoding support and complete Rig framework integration examples with real-world patterns.

🚀 Real-World Use Cases

AI Recruiting Assistant

// Validate candidate profiles before outreach
let result = rig_validate_json("https://linkedin.com/in/john-doe").await;
// Returns: {"valid": true, "username": "john-doe", "confidence": 95, ...}

Lead Generation Bot

// Quick validation for scraped LinkedIn URLs
if rig_is_valid(potential_lead_url).await {
    add_to_crm(potential_lead_url);
}

Professional Network Analyzer

// Get human-readable validation for chat interfaces
let message = rig_validate_text(profile_url).await;
// Returns: "✅ Valid profile @jane-smith (90% confidence)"

Data Enrichment Pipeline

// Batch validate URLs with confidence scoring
let results = urls.iter().map(|url| rig_validate(url)).collect().await;
// Filter by confidence level for data quality

🌟 Key Features

  • 🤖 AI-First Design - Multiple API levels from simple booleans to rich structured data
  • 🎯 Rig Framework Optimized - Ergonomic helpers designed specifically for Rig tools
  • ⚡ Async & Sync APIs - Full async support to prevent blocking runtime panics
  • 📊 Structured Responses - AIValidationResult with confidence scores and decisions
  • 🔍 Smart Validation - Format checking, existence verification, and intelligent fallbacks
  • 📝 Rich Error Context - Detailed explanations with actionable suggestions
  • 🛡️ Never Panics - Comprehensive error handling throughout
  • 🚀 High Performance - Optimized for concurrent operations

📦 Installation

[dependencies]
credify = "0.4.0"

Or use cargo add:

cargo add credify

🚀 Quick Start

For AI Agents & Rig Framework (Recommended)

use credify::{rig_is_valid, rig_validate_text};

// Ultra-simple validation
if rig_is_valid("https://linkedin.com/in/johndoe").await {
    println!("Valid LinkedIn profile!");
}

// Get a human-readable response
let message = rig_validate_text("https://linkedin.com/in/johndoe").await;
// Returns: "✅ Valid profile @johndoe (95% confidence)"

For Rig Tool Implementation

// Complete Rig tool implementation
#[derive(Deserialize)]
struct ValidateLinkedInArgs {
    url: String,
}

#[derive(Deserialize, Serialize)]
struct LinkedInValidator;

#[async_trait]
impl Tool for LinkedInValidator {
    const NAME: &'static str = "validate_linkedin_profile";
    type Args = ValidateLinkedInArgs;
    type Output = String;
    type Error = ToolError;

    async fn definition(&self) -> ToolDefinition {
        ToolDefinition {
            name: Self::NAME.to_string(),
            description: "Validates LinkedIn profile URLs and returns structured information".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "description": "The LinkedIn profile URL to validate"
                    }
                },
                "required": ["url"]
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        // Just one line! No runtime panics, perfect for Rig
        Ok(credify::rig_validate_json(&args.url).await)
    }
}

📚 API Overview

🎯 Ergonomic Rig Helpers (New!)

Function Returns Use Case
rig_is_valid() bool Quick true/false checks
rig_validate_text() String One-line human-readable responses
rig_validate_json() String Clean JSON for tool responses
rig_validate() RigValidationResult Structured data with all details

🤖 AI-Optimized Functions

Function Returns Use Case
ai_validate() AIValidationResult Full structured data
ai_validate_json() String JSON for AI consumption
validate_for_llm() String Verbose text reports

🔧 Traditional API

Function Returns Use Case
is_valid_linkedin_profile_format() bool Format checking only
LinkedInValidator::is_valid_linkedin_profile_url() Result<bool> Full validation

💡 Usage Examples

1. Rig Framework Integration (Recommended)

use credify::{rig_validate, RigValidationResult};
use rig::tool::Tool;

#[derive(Deserialize, Serialize)]
struct LinkedInChecker;

impl Tool for LinkedInChecker {
    const NAME: &'static str = "linkedin_checker";
    type Args = CheckArgs;
    type Output = String;
    type Error = MyError;

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        // One line - that's it!
        Ok(credify::rig_validate_json(&args.url).await)
    }
}

// Or use structured data
async fn check_with_details(url: &str) {
    let result: RigValidationResult = credify::rig_validate(url).await;
    
    println!("Valid: {}", result.valid);
    println!("Status: {}", result.status);
    println!("Action: {}", result.action);
    println!("Confidence: {}%", result.confidence);
    
    if let Some(username) = result.username {
        println!("Username: @{}", username);
    }
}

2. AI Agent Integration

use credify::{ai_validate, AIDecision};

async fn validate_for_ai(url: &str) {
    let result = ai_validate(url).await;
    
    // Simple boolean check
    if result.is_valid {
        println!("Profile is valid!");
    }
    
    // Use confidence for nuanced decisions
    if result.confidence >= 0.9 {
        println!("High confidence validation");
    }
    
    // AI-friendly decision enum
    match result.decision {
        AIDecision::Accept => {
            // Use the profile
            println!("Accepted: {}", result.username.unwrap_or_default());
        }
        AIDecision::Retry => {
            // Network issue, try again
            println!("Temporary issue, retry in a moment");
        }
        AIDecision::Reject => {
            // Invalid URL
            println!("Invalid: {}", result.reason);
        }
    }
}

3. Quick Validation

use credify::is_valid_linkedin_profile_format;

// Format check only (no network calls)
if is_valid_linkedin_profile_format("https://linkedin.com/in/johndoe") {
    println!("Format is valid!");
}

// Full validation with network check
use credify::LinkedInValidator;

let validator = LinkedInValidator::new()?;
match validator.is_valid_linkedin_profile_url(url) {
    Ok(true) => println!("Profile exists!"),
    Ok(false) => println!("Profile not found"),
    Err(e) => println!("Error: {}", e),
}

4. Async Operations

use credify::validate_linkedin_url_async;

// Async validation
let is_valid = validate_linkedin_url_async(url).await?;

// Async with AI response
let json = credify::ai_validate_json_async(url).await;

🎯 Complete Rig Framework Integration Guide

Setting Up Credify with Rig

Credify is designed to work seamlessly with the Rig framework for building AI agents. Here's a complete guide to integrating LinkedIn validation into your Rig-powered AI system.

1. Basic Tool Setup

use credify::rig_validate_json;
use rig::{completion::ToolDefinition, tool::Tool};
use serde::{Deserialize, Serialize};
use serde_json::json;

#[derive(Debug, Deserialize)]
struct ValidateArgs {
    url: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct LinkedInValidator;

#[async_trait::async_trait]
impl Tool for LinkedInValidator {
    const NAME: &'static str = "validate_linkedin_profile";
    type Args = ValidateArgs;
    type Output = String;
    type Error = Box<dyn std::error::Error + Send + Sync>;

    async fn definition(&self) -> ToolDefinition {
        ToolDefinition {
            name: Self::NAME.to_string(),
            description: "Validates LinkedIn profile URLs and returns structured data about the profile".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "description": "The LinkedIn profile URL to validate (e.g., https://linkedin.com/in/username)"
                    }
                },
                "required": ["url"]
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        Ok(rig_validate_json(&args.url).await)
    }
}

2. Using with Rig Agents

use rig::providers::openai;
use rig::completion::{Prompt, ToolDefinition};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create the LinkedIn validator tool
    let linkedin_tool = LinkedInValidator;
    
    // Set up your AI client
    let client = openai::Client::new(&std::env::var("OPENAI_API_KEY")?);
    
    // Create an agent with the LinkedIn validation tool
    let agent = client
        .agent("gpt-4")
        .preamble("You are a professional network analyst.")
        .tool(linkedin_tool)
        .build();
    
    // Use the agent to validate profiles
    let response = agent
        .prompt("Check if https://linkedin.com/in/satya-nadella is a valid LinkedIn profile")
        .await?;
    
    println!("Agent response: {}", response);
    Ok(())
}

3. Advanced Usage with Multiple Response Types

use credify::{rig_is_valid, rig_validate, rig_validate_text};

// Quick boolean check for conditional logic
async fn quick_check(url: &str) -> bool {
    rig_is_valid(url).await
}

// Human-readable response for chat interfaces
async fn chat_response(url: &str) -> String {
    rig_validate_text(url).await
}

// Structured data for complex workflows
async fn detailed_check(url: &str) {
    let result = rig_validate(url).await;
    
    match result.confidence {
        90..=100 => println!("High confidence: proceed with profile"),
        70..=89 => println!("Medium confidence: verify manually"),
        _ => println!("Low confidence: search for alternative")
    }
}

4. Real-World Use Case: Recruiting Assistant

#[derive(Debug, Serialize, Deserialize)]
struct RecruitingAssistant {
    linkedin_validator: LinkedInValidator,
}

impl RecruitingAssistant {
    async fn validate_candidate_profiles(&self, profiles: Vec<String>) -> Vec<CandidateStatus> {
        let mut results = Vec::new();
        
        for profile_url in profiles {
            let validation = rig_validate(&profile_url).await;
            
            results.push(CandidateStatus {
                url: profile_url,
                valid: validation.valid,
                username: validation.username,
                confidence: validation.confidence,
                action: match validation.valid {
                    true => "Schedule interview",
                    false => "Request updated profile"
                }.to_string(),
            });
        }
        
        results
    }
}

#[derive(Debug, Serialize)]
struct CandidateStatus {
    url: String,
    valid: bool,
    username: Option<String>,
    confidence: u8,
    action: String,
}

5. Error Handling Best Practices

// Credify's Rig helpers NEVER panic, making them safe for production
async fn safe_validation(url: &str) -> String {
    // This will always return a valid JSON response, even on errors
    let result = rig_validate_json(url).await;
    
    // Parse the result to handle different scenarios
    if let Ok(parsed) = serde_json::from_str::<RigValidationResult>(&result) {
        if parsed.valid {
            format!("Profile @{} is valid", parsed.username.unwrap_or_default())
        } else {
            format!("Invalid profile: {}", parsed.status)
        }
    } else {
        "Validation service temporarily unavailable".to_string()
    }
}

6. Batch Processing with Rig

use futures::future::join_all;

async fn batch_validate(urls: Vec<String>) -> Vec<(String, bool)> {
    let futures = urls.into_iter().map(|url| async move {
        let valid = rig_is_valid(&url).await;
        (url, valid)
    });
    
    join_all(futures).await
}

Function Calling Patterns

Pattern 1: Simple Validation Tool

// Minimal implementation for basic needs
impl Tool for SimpleLinkedInChecker {
    async fn call(&self, args: Args) -> Result<String, Error> {
        if rig_is_valid(&args.url).await {
            Ok("Valid LinkedIn profile".to_string())
        } else {
            Ok("Invalid LinkedIn profile".to_string())
        }
    }
}

Pattern 2: Detailed Analysis Tool

// Rich responses for complex AI workflows
impl Tool for DetailedLinkedInAnalyzer {
    async fn call(&self, args: Args) -> Result<String, Error> {
        let result = rig_validate(&args.url).await;
        
        Ok(json!({
            "valid": result.valid,
            "username": result.username,
            "confidence": result.confidence,
            "status": result.status,
            "recommended_action": result.action,
            "profile_url": if result.valid {
                Some(format!("https://linkedin.com/in/{}", 
                    result.username.as_ref().unwrap_or(&"unknown".to_string())))
            } else {
                None
            }
        }).to_string())
    }
}

Pattern 3: Multi-Step Validation

// Complex validation with fallback strategies
impl Tool for SmartLinkedInValidator {
    async fn call(&self, args: Args) -> Result<String, Error> {
        // First, try the provided URL
        let result = rig_validate(&args.url).await;
        
        if result.valid {
            return Ok(rig_validate_json(&args.url).await);
        }
        
        // If invalid, try common variations
        if let Some(username) = extract_username(&args.url) {
            let variations = vec![
                format!("https://linkedin.com/in/{}", username),
                format!("https://www.linkedin.com/in/{}", username),
                format!("https://linkedin.com/in/{}/", username),
            ];
            
            for variant in variations {
                if rig_is_valid(&variant).await {
                    return Ok(rig_validate_json(&variant).await);
                }
            }
        }
        
        // Return detailed error information
        Ok(json!({
            "valid": false,
            "error": "Profile not found",
            "suggestions": [
                "Check the username spelling",
                "Verify the profile hasn't been deleted",
                "Try searching by name on LinkedIn"
            ]
        }).to_string())
    }
}

Integration Tips

  1. Always use async: All Rig helpers are async to prevent blocking
  2. Never panics: Rig helpers return valid responses even on errors
  3. Structured responses: Use rig_validate_json for consistent AI parsing
  4. Confidence scores: Use confidence levels to make nuanced decisions
  5. Batch wisely: Process multiple URLs concurrently for better performance

⚠️ Important: Async Usage

When using Credify in async contexts (like web servers or AI frameworks), always use the async versions to avoid runtime panics:

// ❌ WRONG - Can cause panic in async context
async fn my_tool() {
    let result = credify::ai_validate_json(url); // Panic!
}

// ✅ CORRECT - Use async version
async fn my_tool() {
    let result = credify::ai_validate_json_async(url).await; // Works!
}

// ✅ BEST - Use Rig helpers (always async)
async fn my_tool() {
    let result = credify::rig_validate_json(url).await; // Perfect!
}

📊 Response Types

RigValidationResult

pub struct RigValidationResult {
    pub valid: bool,              // Simple pass/fail
    pub username: Option<String>, // LinkedIn username if found
    pub confidence: u8,           // 0-100 percentage
    pub status: String,           // Human-readable status
    pub action: String,           // Suggested action for AI
}

AIValidationResult

pub struct AIValidationResult {
    pub is_valid: bool,
    pub confidence: f32,          // 0.0 to 1.0
    pub decision: AIDecision,     // Accept/Retry/Reject
    pub username: Option<String>,
    pub reason: String,
    pub metadata: ValidationMetadata,
}

🤖 Why AI-Friendly Validation Matters

Traditional validation returns simple true/false or error codes. AI agents need rich context to make intelligent decisions:

  • Context-Rich Responses: Understand why validation failed
  • Confidence Scores: Make nuanced decisions based on certainty
  • Actionable Suggestions: Know what to do next
  • Structured Data: Easy to parse and reason about

🛠️ Advanced Features

Custom User Agent

let validator = LinkedInValidator::new_with_user_agent(
    "MyBot/1.0 (https://mybot.com)"
)?;

Handling LinkedIn Authentication

LinkedIn often returns AUTH_REQUIRED (999 status) for valid profiles. Credify intelligently handles this:

// AUTH_REQUIRED is treated as a valid profile
let result = rig_validate(url).await;
if result.valid && result.status.contains("auth required") {
    println!("Profile likely exists but LinkedIn is blocking checks");
}

📖 More Examples

Check out the examples/ directory for:

  • basic.rs - Simple validation examples
  • rig_ergonomic.rs - Ergonomic Rig API showcase
  • rig_integration.rs - Full Rig framework integration with function calling
  • rig_async_proper.rs - Advanced async patterns for Rig
  • batch_validator.rs - Validate multiple URLs concurrently
  • llm_simple.rs - LLM-friendly validation
  • ai_agent_demo.rs - Complete AI agent implementation

Quick Example: Rig Function Calling

// Define your tool
#[derive(Tool)]
#[tool(
    name = "linkedin_validator",
    description = "Validates LinkedIn profile URLs"
)]
struct LinkedInValidator;

// Implement the tool - just one line!
impl LinkedInValidator {
    async fn validate(&self, url: String) -> String {
        credify::rig_validate_json(&url).await
    }
}

// Use with your AI agent
let agent = client
    .agent("gpt-4")
    .tool(LinkedInValidator)
    .build();

let response = agent
    .prompt("Check these LinkedIn profiles for our hiring pipeline")
    .await?;

Run examples with:

cargo run --example rig_ergonomic

🧪 Testing

# Run all tests
cargo test

# Run with verbose output
cargo test -- --nocapture

📄 License

Licensed under either of:

at your option.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🙏 Acknowledgments

Built with ❤️ for the AI agent community. Special thanks to the Rig framework team for inspiring the ergonomic API design.


Built by Hamze Ghalebi

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages