Skip to content

Commit 9261063

Browse files
committed
feat: Add AI-optimized API for LLM agents and Rig framework
- Add structured types: AIValidationResult, AIDecision, ValidationMetadata - Add ai_validate() and ai_validate_async() functions for AI agents - Add JSON output functions: ai_validate_json() and ai_validate_json_async() - Add confidence levels (0.0-1.0) for nuanced AI decision-making - Add Rig framework integration example - Support LinkedIn usernames with dots (e.g., first.last) - Update to Rust 2024 edition - Bump version to 0.3.0
1 parent 820e799 commit 9261063

File tree

7 files changed

+1062
-23
lines changed

7 files changed

+1062
-23
lines changed

CHANGELOG.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.3.0] - 2025-07-31
11+
12+
### Added
13+
- AI-optimized API with structured types (`AIValidationResult`, `AIDecision`, `ValidationMetadata`)
14+
- `ai_validate()` and `ai_validate_async()` functions returning structured data for AI agents
15+
- `ai_validate_json()` and `ai_validate_json_async()` for direct JSON output
16+
- Confidence levels (0.0 to 1.0) for nuanced AI decision-making
17+
- Decision enum (Accept/Retry/Reject) for clear agent actions
18+
- Comprehensive tests for AI-optimized functions
19+
- Rig framework integration example demonstrating AI tool implementation
20+
- Support for LinkedIn usernames with dots (e.g., first.last)
21+
22+
### Changed
23+
- Enhanced README with AI-optimized API documentation and Rig framework integration guide
24+
- Improved username extraction to properly handle all valid LinkedIn username formats
25+
- Updated to Rust 2024 edition
26+
1027
## [0.2.1] - 2025-07-31
1128

1229
### Fixed
@@ -16,6 +33,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1633

1734
### Changed
1835
- Made CodeCov optional in CI to prevent build failures
36+
- Improved AUTH_REQUIRED suggested actions for AI agents - now recommends accepting URLs as valid
37+
- Updated detailed explanations to clarify that AUTH_REQUIRED likely means the profile exists
38+
39+
### Added
40+
- New example `ai_agent.rs` demonstrating usage with AI agents for lead generation
1941

2042
## [0.2.0] - 2025-07-31
2143

@@ -60,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6082
- Safe handling of all network errors
6183
- Proper timeout configuration for HTTP requests
6284

63-
[Unreleased]: https://github.com/RustSandbox/Credify/compare/v0.2.1...HEAD
85+
[Unreleased]: https://github.com/RustSandbox/Credify/compare/v0.3.0...HEAD
86+
[0.3.0]: https://github.com/RustSandbox/Credify/compare/v0.2.1...v0.3.0
6487
[0.2.1]: https://github.com/RustSandbox/Credify/compare/v0.2.0...v0.2.1
6588
[0.2.0]: https://github.com/RustSandbox/Credify/compare/v0.1.0...v0.2.0
6689
[0.1.0]: https://github.com/RustSandbox/Credify/releases/tag/v0.1.0

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "credify"
3-
version = "0.2.1"
4-
edition = "2021"
5-
rust-version = "1.70"
3+
version = "0.3.0"
4+
edition = "2024"
5+
rust-version = "1.85"
66
authors = ["Hamze Ghalebi <hamze.ghalebi@gmail.com>"]
77
license = "MIT OR Apache-2.0"
88
description = "A Rust library for validating LinkedIn profile URLs with LLM-friendly error messages"
@@ -36,6 +36,8 @@ thiserror = "2.0"
3636
tokio = { version = "1.45.1", features = ["rt-multi-thread", "macros"] }
3737
once_cell = "1.20"
3838
chrono = "0.4.41"
39+
serde = { version = "1.0", features = ["derive"] }
40+
serde_json = "1.0"
3941

4042
[dev-dependencies]
4143
pretty_assertions = "1.4"

README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,79 @@ let result = validate_for_llm("https://linkedin.com/in/johndoe");
199199
// 5. Provide helpful feedback to end users
200200
```
201201

202+
### 🚀 AI-Optimized API (New in v0.2.1!)
203+
204+
For even deeper AI agent integration, Credify now offers structured data types specifically designed for AI decision-making:
205+
206+
```rust
207+
use credify::{ai_validate, ai_validate_json, AIDecision};
208+
209+
// Get structured validation result
210+
let result = ai_validate("https://linkedin.com/in/johndoe");
211+
212+
// Simple boolean for quick decisions
213+
if result.is_valid {
214+
println!("Valid profile!");
215+
}
216+
217+
// Confidence level (0.0 to 1.0) for nuanced decisions
218+
if result.confidence >= 0.8 {
219+
println!("High confidence: {}", result.confidence);
220+
}
221+
222+
// AI-friendly decision enum
223+
match result.decision {
224+
AIDecision::Accept => {
225+
// Use the profile URL
226+
println!("Profile accepted: {:?}", result.username);
227+
}
228+
AIDecision::Retry => {
229+
// Network issue - try again later
230+
println!("Temporary issue, retry suggested");
231+
}
232+
AIDecision::Reject => {
233+
// Invalid URL - search for another
234+
println!("Invalid URL: {}", result.reason);
235+
}
236+
}
237+
238+
// Get JSON for direct AI consumption
239+
let json = ai_validate_json("https://linkedin.com/in/johndoe");
240+
// Returns pretty-printed JSON with all fields
241+
```
242+
243+
#### Integration with Rig Framework
244+
245+
Credify is optimized for use with the [Rig](https://github.com/0xPlaygrounds/rig) Rust framework for building AI agents:
246+
247+
```rust
248+
// Define the tool for Rig
249+
#[derive(Deserialize, Serialize)]
250+
struct LinkedInValidator;
251+
252+
impl Tool for LinkedInValidator {
253+
const NAME: &'static str = "LinkedInValidator";
254+
255+
async fn call(&self, args: ValidateLinkedInArgs) -> Result<String, Error> {
256+
// Use the AI-optimized JSON function
257+
Ok(credify::ai_validate_json(&args.url))
258+
}
259+
}
260+
261+
// System prompt for LinkedIn lead generation
262+
const SYSTEM_PROMPT: &str = r#"
263+
When validating LinkedIn profiles:
264+
1. Use web search to find LinkedIn URLs - don't make them up
265+
2. ALWAYS validate URLs with LinkedInValidator before using them
266+
3. Interpret results:
267+
- is_valid: true + confidence >= 0.9 = Definitely real profile
268+
- decision: Accept = Use this URL
269+
- decision: Retry = Try again in a few seconds
270+
- decision: Reject = Search for a different URL
271+
4. AUTH_REQUIRED usually means the profile EXISTS
272+
"#;
273+
```
274+
202275
Common LLM agent scenarios:
203276
- **Lead Generation**: Validate profiles before adding to CRM
204277
- **Data Enrichment**: Verify profiles before fetching additional data

examples/ai_agent.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//! Example demonstrating how to use Credify with AI agents for lead generation
2+
//!
3+
//! This example shows how the validate_for_llm function is designed to work
4+
//! with AI agents that need to validate LinkedIn URLs found during searches.
5+
6+
use credify::validate_for_llm;
7+
8+
/// Simulated AI agent tool for LinkedIn profile validation
9+
struct LinkedInValidatorTool;
10+
11+
impl LinkedInValidatorTool {
12+
/// Validate a LinkedIn URL and return AI-friendly response
13+
fn validate(&self, url: &str) -> String {
14+
// The validate_for_llm function returns verbose structured data
15+
// but does NOT print to terminal - perfect for AI agents
16+
validate_for_llm(url)
17+
}
18+
}
19+
20+
fn main() {
21+
println!("LinkedIn Profile Validator for AI Agents Example\n");
22+
23+
let tool = LinkedInValidatorTool;
24+
25+
// Simulate URLs that an AI agent might find during lead generation
26+
let test_urls = vec![
27+
(
28+
"https://www.linkedin.com/in/johnsmith",
29+
"Valid profile format",
30+
),
31+
("https://linkedin.com/in/jane-doe-123", "Valid with hyphens"),
32+
("https://www.google.com/in/someone", "Wrong domain"),
33+
(
34+
"https://linkedin.com/company/microsoft",
35+
"Company page, not profile",
36+
),
37+
("not-a-url", "Invalid URL format"),
38+
];
39+
40+
for (url, description) in test_urls {
41+
println!("=== Testing: {} ===", description);
42+
println!("URL: {}\n", url);
43+
44+
// Call the tool - this returns structured data but doesn't print
45+
let result = tool.validate(url);
46+
47+
// Parse the result to demonstrate what an AI agent would see
48+
let is_success = result.contains("VALIDATION_RESULT: SUCCESS");
49+
let is_auth_required = result.contains("ERROR_TYPE: AUTH_REQUIRED");
50+
51+
if is_success {
52+
println!("✅ AI Agent Decision: VALID PROFILE - Proceed with this URL");
53+
} else if is_auth_required {
54+
println!("✅ AI Agent Decision: LIKELY VALID - LinkedIn is blocking checks but URL format is correct");
55+
println!(" Recommendation: Treat as valid profile (LinkedIn auth walls usually mean real profiles)");
56+
} else {
57+
println!("❌ AI Agent Decision: INVALID - Search for a different URL");
58+
}
59+
60+
// Show key information the AI would parse
61+
for line in result.lines() {
62+
if line.starts_with("VALIDATION_RESULT:")
63+
|| line.starts_with("ERROR_TYPE:")
64+
|| line.starts_with("RECOMMENDED_NEXT_STEP:")
65+
{
66+
println!(" {}", line);
67+
}
68+
}
69+
70+
println!("\n---\n");
71+
}
72+
73+
// Demonstrate the authentication case specifically
74+
println!("=== Special Case: LinkedIn Authentication Required ===\n");
75+
println!("When LinkedIn returns AUTH_REQUIRED:");
76+
println!("- The URL format is valid");
77+
println!("- The profile LIKELY EXISTS");
78+
println!("- LinkedIn is just blocking automated checks");
79+
println!("- AI agents should ACCEPT these URLs as valid");
80+
println!("\nThis is the most common response for real LinkedIn profiles!");
81+
}

examples/rig_integration.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//! Example of using Credify with Rig framework for AI-powered lead generation
2+
//!
3+
//! This example demonstrates the AI-optimized API designed specifically
4+
//! for function calling with LLM agents.
5+
6+
use credify::{ai_validate_json, AIDecision, AIValidationResult};
7+
use serde::{Deserialize, Serialize};
8+
use serde_json::json;
9+
10+
// Note: This is a mock implementation of Rig tool trait
11+
// In real usage, you would import from rig crate
12+
13+
#[derive(Deserialize)]
14+
struct ValidateLinkedInArgs {
15+
url: String,
16+
}
17+
18+
#[derive(Debug, thiserror::Error)]
19+
#[error("LinkedIn validation error")]
20+
struct LinkedInValidatorError(String);
21+
22+
#[derive(Deserialize, Serialize)]
23+
struct LinkedInValidator;
24+
25+
// Mock implementation of Rig Tool trait
26+
impl LinkedInValidator {
27+
const NAME: &'static str = "LinkedInValidator";
28+
29+
fn definition(&self) -> serde_json::Value {
30+
json!({
31+
"name": "LinkedInValidator",
32+
"description": "Validates LinkedIn profile URLs and returns structured data for AI decision making",
33+
"parameters": {
34+
"type": "object",
35+
"properties": {
36+
"url": {
37+
"type": "string",
38+
"description": "The LinkedIn URL to validate"
39+
}
40+
},
41+
"required": ["url"]
42+
}
43+
})
44+
}
45+
46+
async fn call(&self, args: ValidateLinkedInArgs) -> Result<String, LinkedInValidatorError> {
47+
// Use the AI-optimized JSON function
48+
let json_result = tokio::task::spawn_blocking(move || ai_validate_json(&args.url))
49+
.await
50+
.map_err(|e| LinkedInValidatorError(format!("Task error: {}", e)))?;
51+
52+
Ok(json_result)
53+
}
54+
}
55+
56+
// Example system prompt optimized for LinkedIn lead generation
57+
fn lead_gen_system_prompt() -> &'static str {
58+
r#"
59+
You are an expert LinkedIn lead generator. When searching for LinkedIn profiles:
60+
61+
1. Use web search to find actual LinkedIn URLs - don't make them up
62+
2. When you find a URL, ALWAYS validate it using the LinkedInValidator tool
63+
3. Interpret the validation results:
64+
- is_valid: true with confidence >= 0.9 = DEFINITELY a real profile
65+
- is_valid: true with confidence >= 0.7 = LIKELY a real profile
66+
- decision: Accept = Use this URL
67+
- decision: Retry = Try validating again after a few seconds
68+
- decision: Reject = Search for a different URL
69+
4. AUTH_REQUIRED errors usually mean the profile EXISTS - treat these as valid
70+
5. Extract the username from the result for your records
71+
72+
Always provide the validated LinkedIn URL to the user along with the confidence level.
73+
"#
74+
}
75+
76+
#[tokio::main]
77+
async fn main() {
78+
println!("=== Credify + Rig Framework Integration Example ===\n");
79+
80+
// Demonstrate the AI-optimized validation
81+
let test_urls = vec![
82+
"https://www.linkedin.com/in/satyanadella",
83+
"https://linkedin.com/in/fake-user-12345",
84+
"https://www.google.com/in/someone",
85+
"not-a-url",
86+
];
87+
88+
let validator = LinkedInValidator;
89+
90+
for url in test_urls {
91+
println!("Testing URL: {}", url);
92+
93+
let args = ValidateLinkedInArgs {
94+
url: url.to_string(),
95+
};
96+
97+
match validator.call(args).await {
98+
Ok(json_result) => {
99+
// Parse the JSON result
100+
match serde_json::from_str::<AIValidationResult>(&json_result) {
101+
Ok(result) => {
102+
println!(" ✓ Validation complete:");
103+
println!(" - Valid: {}", result.is_valid);
104+
println!(" - Confidence: {:.1}%", result.confidence * 100.0);
105+
println!(" - Decision: {:?}", result.decision);
106+
println!(" - Reason: {}", result.reason);
107+
108+
if let Some(username) = &result.username {
109+
println!(" - Username: {}", username);
110+
}
111+
112+
// Show AI agent interpretation
113+
match result.decision {
114+
AIDecision::Accept => {
115+
println!(" 🤖 AI: Use this LinkedIn URL");
116+
}
117+
AIDecision::Retry => {
118+
println!(" 🤖 AI: Network issue - retry in a few seconds");
119+
}
120+
AIDecision::Reject => {
121+
println!(" 🤖 AI: Invalid URL - search for another");
122+
}
123+
}
124+
}
125+
Err(e) => {
126+
println!(" ✗ Failed to parse result: {}", e);
127+
}
128+
}
129+
}
130+
Err(e) => {
131+
println!(" ✗ Validation error: {}", e);
132+
}
133+
}
134+
135+
println!();
136+
}
137+
138+
// Show the system prompt
139+
println!("\n=== Recommended System Prompt for Rig ===");
140+
println!("{}", lead_gen_system_prompt());
141+
142+
// Demonstrate JSON output format
143+
println!("\n=== JSON Output Format Example ===");
144+
let example_json = ai_validate_json("https://linkedin.com/in/example");
145+
println!("{}", example_json);
146+
}

0 commit comments

Comments
 (0)