| Crates.io | credify |
| lib.rs | credify |
| version | 0.4.0 |
| created_at | 2025-07-31 13:39:50.232845+00 |
| updated_at | 2025-07-31 16:16:32.072122+00 |
| description | A Rust library for validating LinkedIn profile URLs with LLM-friendly error messages |
| homepage | https://github.com/RustSandbox/Credify |
| repository | https://github.com/RustSandbox/Credify |
| max_upload_size | |
| id | 1775063 |
| size | 292,322 |
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.
// 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, ...}
// Quick validation for scraped LinkedIn URLs
if rig_is_valid(potential_lead_url).await {
add_to_crm(potential_lead_url);
}
// Get human-readable validation for chat interfaces
let message = rig_validate_text(profile_url).await;
// Returns: "โ
Valid profile @jane-smith (90% confidence)"
// Batch validate URLs with confidence scoring
let results = urls.iter().map(|url| rig_validate(url)).collect().await;
// Filter by confidence level for data quality
AIValidationResult with confidence scores and decisions[dependencies]
credify = "0.4.0"
Or use cargo add:
cargo add credify
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)"
// 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)
}
}
| 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 |
| 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 |
| Function | Returns | Use Case |
|---|---|---|
is_valid_linkedin_profile_format() |
bool |
Format checking only |
LinkedInValidator::is_valid_linkedin_profile_url() |
Result<bool> |
Full validation |
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);
}
}
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);
}
}
}
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),
}
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;
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.
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)
}
}
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(())
}
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")
}
}
#[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,
}
// 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()
}
}
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
}
// 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())
}
}
}
// 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())
}
}
// 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())
}
}
rig_validate_json for consistent AI parsingWhen 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!
}
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
}
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,
}
Traditional validation returns simple true/false or error codes. AI agents need rich context to make intelligent decisions:
let validator = LinkedInValidator::new_with_user_agent(
"MyBot/1.0 (https://mybot.com)"
)?;
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");
}
Check out the examples/ directory for:
basic.rs - Simple validation examplesrig_ergonomic.rs - Ergonomic Rig API showcaserig_integration.rs - Full Rig framework integration with function callingrig_async_proper.rs - Advanced async patterns for Rigbatch_validator.rs - Validate multiple URLs concurrentlyllm_simple.rs - LLM-friendly validationai_agent_demo.rs - Complete AI agent implementation// 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
# Run all tests
cargo test
# Run with verbose output
cargo test -- --nocapture
Licensed under either of:
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.
Built with โค๏ธ for the AI agent community. Special thanks to the Rig framework team for inspiring the ergonomic API design.