@the_ro_show/agent-ads-sdk 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -4
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +59 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +59 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -500,7 +500,17 @@ const ad = await client.decideFromContext({
|
|
|
500
500
|
- Example: Blocking `1` (Automotive) blocks Auto Insurance, Auto Repair, etc.
|
|
501
501
|
- Great for compliance: Block `601` (Sensitive Topics) to block gambling, adult, controversial in one go
|
|
502
502
|
|
|
503
|
-
**
|
|
503
|
+
**Important:**
|
|
504
|
+
- If `allowedCategories` is set, `blockedCategories` is ignored
|
|
505
|
+
- IAB category IDs (numbers) are **recommended** - legacy string categories are deprecated (support ends 2026-06-01)
|
|
506
|
+
- Use `getCategories()` API to discover category IDs
|
|
507
|
+
- Empty `allowedCategories: []` is rejected (would block all ads - use `blockedCategories` instead)
|
|
508
|
+
|
|
509
|
+
**Validation rules:**
|
|
510
|
+
- `minQualityScore`: Must be 0.0-1.0
|
|
511
|
+
- `allowedCategories`: Must be non-empty array of strings or numbers
|
|
512
|
+
- `blockedCategories`: Must be array of strings or numbers
|
|
513
|
+
- `blockedAdvertisers`: Must be array of non-empty strings (advertiser IDs)
|
|
504
514
|
|
|
505
515
|
### Advertiser Blocklist
|
|
506
516
|
|
|
@@ -515,16 +525,111 @@ const ad = await client.decideFromContext({
|
|
|
515
525
|
|
|
516
526
|
Get advertiser IDs from previous ad responses.
|
|
517
527
|
|
|
518
|
-
|
|
528
|
+
## 💰 Revenue Optimization (v0.13.1+)
|
|
529
|
+
|
|
530
|
+
Control revenue vs. relevance tradeoffs with bid and relevance filters.
|
|
531
|
+
|
|
532
|
+
### Minimum CPC Filter
|
|
533
|
+
|
|
534
|
+
Only show ads above a minimum bid threshold:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
const ad = await client.decideFromContext({
|
|
538
|
+
userMessage: "I need car insurance",
|
|
539
|
+
minCPC: 100 // Only ads bidding ≥$1.00 per click
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Use cases:**
|
|
544
|
+
- **Premium apps**: `minCPC: 200` for $2+ ads only
|
|
545
|
+
- **High-value verticals**: Filter out low-budget advertisers
|
|
546
|
+
- **Revenue targets**: Ensure minimum revenue per impression
|
|
547
|
+
|
|
548
|
+
### Minimum Relevance Score
|
|
549
|
+
|
|
550
|
+
Only show ads that are semantically relevant:
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
const ad = await client.decideFromContext({
|
|
554
|
+
userMessage: "Help me plan my wedding",
|
|
555
|
+
minRelevanceScore: 0.8 // Only highly relevant ads (0.0-1.0)
|
|
556
|
+
});
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Use cases:**
|
|
560
|
+
- **Niche apps**: Legal assistant = `0.8+` for specialized content only
|
|
561
|
+
- **User experience**: Filter out loosely related ads
|
|
562
|
+
- **Context matching**: Ensure ads match conversation topic
|
|
563
|
+
- **Default threshold**: Backend uses 0.25 minimum
|
|
564
|
+
|
|
565
|
+
### Ranking Strategy
|
|
566
|
+
|
|
567
|
+
Choose how ads are ranked:
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// Default: Revenue-optimized (highest bid wins)
|
|
571
|
+
const ad = await client.decideFromContext({
|
|
572
|
+
userMessage: "I need legal help",
|
|
573
|
+
optimizeFor: 'revenue' // Rank by bid × quality × relevance
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// Alternative: Relevance-optimized (best match wins)
|
|
577
|
+
const ad = await client.decideFromContext({
|
|
578
|
+
userMessage: "I need legal help",
|
|
579
|
+
optimizeFor: 'relevance' // Rank by semantic similarity only
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**Use cases:**
|
|
584
|
+
- **General apps**: `'revenue'` to maximize earnings
|
|
585
|
+
- **Niche apps**: `'relevance'` to prioritize perfect matches over high bids
|
|
586
|
+
- **Premium experiences**: Combine with high `minRelevanceScore` + `'relevance'` ranking
|
|
587
|
+
|
|
588
|
+
**How it works (second-price auction):**
|
|
589
|
+
- **Revenue mode**: Winner ranked by composite score (bid × quality × relevance), pays just enough to beat next ad's composite score + $0.01
|
|
590
|
+
- **Relevance mode**: Winner ranked by semantic similarity, pays just enough to beat next ad in the composite score space + $0.01
|
|
591
|
+
- **Always capped**: Winner never pays more than their max bid (auction integrity guaranteed)
|
|
592
|
+
- **Floor price**: Minimum $0.25 clearing price ensures platform revenue
|
|
593
|
+
|
|
594
|
+
**Important notes:**
|
|
595
|
+
- `minRelevanceScore` only applies to campaigns with semantic targeting
|
|
596
|
+
- For keyword/automatic campaigns, relevance filter has no effect
|
|
597
|
+
- Validation errors return HTTP 400 with clear messages
|
|
598
|
+
|
|
599
|
+
### Combined Revenue Controls
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
// Premium legal assistant: High relevance + high bids
|
|
603
|
+
const ad = await client.decideFromContext({
|
|
604
|
+
userMessage: "I need estate planning help",
|
|
605
|
+
minRelevanceScore: 0.8, // Only highly relevant
|
|
606
|
+
minCPC: 200, // Only $2+ bids
|
|
607
|
+
optimizeFor: 'relevance', // Best match wins
|
|
608
|
+
allowedCategories: [318] // Legal services only (IAB)
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// General chatbot: Maximize revenue
|
|
612
|
+
const ad = await client.decideFromContext({
|
|
613
|
+
userMessage: "Help me with something",
|
|
614
|
+
optimizeFor: 'revenue', // Highest bid wins
|
|
615
|
+
minQualityScore: 0.7 // Decent quality threshold
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Combined Controls (All Phases)
|
|
519
620
|
|
|
520
621
|
Mix and match for precise control:
|
|
521
622
|
|
|
522
623
|
```typescript
|
|
523
624
|
const ad = await client.decideFromContext({
|
|
524
625
|
userMessage: "I need car insurance",
|
|
626
|
+
// Phase 1: Quality & Brand Safety
|
|
525
627
|
minQualityScore: 0.8, // High quality only
|
|
526
|
-
|
|
527
|
-
|
|
628
|
+
allowedCategories: [31, 398], // Auto + Personal Finance Insurance (IAB)
|
|
629
|
+
// Phase 2: Revenue Optimization
|
|
630
|
+
minCPC: 50, // $0.50+ bids only
|
|
631
|
+
minRelevanceScore: 0.7, // Highly relevant only
|
|
632
|
+
optimizeFor: 'revenue' // Highest bid wins
|
|
528
633
|
});
|
|
529
634
|
```
|
|
530
635
|
|
package/dist/index.d.mts
CHANGED
|
@@ -96,6 +96,35 @@ interface DecideFromContextRequest {
|
|
|
96
96
|
* blockedAdvertisers: ['adv_abc123', 'adv_xyz789']
|
|
97
97
|
*/
|
|
98
98
|
blockedAdvertisers?: string[];
|
|
99
|
+
/**
|
|
100
|
+
* Minimum cost-per-click threshold in cents.
|
|
101
|
+
* Only return ads with bids at or above this value.
|
|
102
|
+
* Higher values = more revenue per ad but lower fill rate.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* minCPC: 50 // Only show ads bidding $0.50 or more per click
|
|
106
|
+
*/
|
|
107
|
+
minCPC?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Minimum semantic relevance score threshold (0.0 - 1.0).
|
|
110
|
+
* Only return ads with relevance scores at or above this value.
|
|
111
|
+
* Higher values = more relevant ads but lower fill rate.
|
|
112
|
+
* Default backend threshold: 0.25
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* minRelevanceScore: 0.8 // Only show highly relevant ads
|
|
116
|
+
*/
|
|
117
|
+
minRelevanceScore?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Ad ranking strategy.
|
|
120
|
+
* - 'revenue': Rank by bid amount (highest bid wins)
|
|
121
|
+
* - 'relevance': Rank by semantic similarity (best match wins)
|
|
122
|
+
* Default: 'revenue'
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* optimizeFor: 'relevance' // Prioritize best semantic match over highest bid
|
|
126
|
+
*/
|
|
127
|
+
optimizeFor?: 'revenue' | 'relevance';
|
|
99
128
|
}
|
|
100
129
|
interface DecideResponse {
|
|
101
130
|
request_id: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,35 @@ interface DecideFromContextRequest {
|
|
|
96
96
|
* blockedAdvertisers: ['adv_abc123', 'adv_xyz789']
|
|
97
97
|
*/
|
|
98
98
|
blockedAdvertisers?: string[];
|
|
99
|
+
/**
|
|
100
|
+
* Minimum cost-per-click threshold in cents.
|
|
101
|
+
* Only return ads with bids at or above this value.
|
|
102
|
+
* Higher values = more revenue per ad but lower fill rate.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* minCPC: 50 // Only show ads bidding $0.50 or more per click
|
|
106
|
+
*/
|
|
107
|
+
minCPC?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Minimum semantic relevance score threshold (0.0 - 1.0).
|
|
110
|
+
* Only return ads with relevance scores at or above this value.
|
|
111
|
+
* Higher values = more relevant ads but lower fill rate.
|
|
112
|
+
* Default backend threshold: 0.25
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* minRelevanceScore: 0.8 // Only show highly relevant ads
|
|
116
|
+
*/
|
|
117
|
+
minRelevanceScore?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Ad ranking strategy.
|
|
120
|
+
* - 'revenue': Rank by bid amount (highest bid wins)
|
|
121
|
+
* - 'relevance': Rank by semantic similarity (best match wins)
|
|
122
|
+
* Default: 'revenue'
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* optimizeFor: 'relevance' // Prioritize best semantic match over highest bid
|
|
126
|
+
*/
|
|
127
|
+
optimizeFor?: 'revenue' | 'relevance';
|
|
99
128
|
}
|
|
100
129
|
interface DecideResponse {
|
|
101
130
|
request_id: string;
|
package/dist/index.js
CHANGED
|
@@ -503,6 +503,60 @@ var AttentionMarketClient = class {
|
|
|
503
503
|
const platform = params.platform || "web";
|
|
504
504
|
const placementType = params.placement || "sponsored_suggestion";
|
|
505
505
|
const taxonomy = params.suggestedCategory || this.inferTaxonomy(params.userMessage);
|
|
506
|
+
if (params.minQualityScore !== void 0) {
|
|
507
|
+
if (typeof params.minQualityScore !== "number" || params.minQualityScore < 0 || params.minQualityScore > 1) {
|
|
508
|
+
throw new Error("minQualityScore must be a number between 0.0 and 1.0");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (params.allowedCategories !== void 0) {
|
|
512
|
+
if (!Array.isArray(params.allowedCategories)) {
|
|
513
|
+
throw new Error("allowedCategories must be an array");
|
|
514
|
+
}
|
|
515
|
+
if (params.allowedCategories.length === 0) {
|
|
516
|
+
throw new Error("allowedCategories cannot be empty (would block all ads). Use blockedCategories to exclude specific categories, or omit to allow all.");
|
|
517
|
+
}
|
|
518
|
+
for (const cat of params.allowedCategories) {
|
|
519
|
+
if (typeof cat !== "string" && typeof cat !== "number") {
|
|
520
|
+
throw new Error(`allowedCategories must contain strings or numbers, found: ${typeof cat}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (params.blockedCategories !== void 0) {
|
|
525
|
+
if (!Array.isArray(params.blockedCategories)) {
|
|
526
|
+
throw new Error("blockedCategories must be an array");
|
|
527
|
+
}
|
|
528
|
+
for (const cat of params.blockedCategories) {
|
|
529
|
+
if (typeof cat !== "string" && typeof cat !== "number") {
|
|
530
|
+
throw new Error(`blockedCategories must contain strings or numbers, found: ${typeof cat}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (params.allowedCategories && params.allowedCategories.length > 0) {
|
|
534
|
+
console.warn("[AttentionMarket] Both allowedCategories and blockedCategories specified. blockedCategories will be ignored.");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (params.blockedAdvertisers !== void 0) {
|
|
538
|
+
if (!Array.isArray(params.blockedAdvertisers)) {
|
|
539
|
+
throw new Error("blockedAdvertisers must be an array");
|
|
540
|
+
}
|
|
541
|
+
for (const id of params.blockedAdvertisers) {
|
|
542
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
543
|
+
throw new Error("blockedAdvertisers must contain non-empty strings (advertiser IDs)");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (params.minCPC !== void 0) {
|
|
548
|
+
if (typeof params.minCPC !== "number" || params.minCPC < 0) {
|
|
549
|
+
throw new Error("minCPC must be a non-negative number (cost-per-click in cents)");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (params.minRelevanceScore !== void 0) {
|
|
553
|
+
if (typeof params.minRelevanceScore !== "number" || params.minRelevanceScore < 0 || params.minRelevanceScore > 1) {
|
|
554
|
+
throw new Error("minRelevanceScore must be a number between 0.0 and 1.0");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (params.optimizeFor && params.optimizeFor !== "revenue" && params.optimizeFor !== "relevance") {
|
|
558
|
+
throw new Error('optimizeFor must be either "revenue" or "relevance"');
|
|
559
|
+
}
|
|
506
560
|
const request = {
|
|
507
561
|
request_id: generateUUID(),
|
|
508
562
|
agent_id: this.agentId,
|
|
@@ -534,7 +588,11 @@ var AttentionMarketClient = class {
|
|
|
534
588
|
...params.minQualityScore !== void 0 && { minQualityScore: params.minQualityScore },
|
|
535
589
|
...params.allowedCategories && { allowedCategories: params.allowedCategories },
|
|
536
590
|
...params.blockedCategories && { blockedCategories: params.blockedCategories },
|
|
537
|
-
...params.blockedAdvertisers && { blockedAdvertisers: params.blockedAdvertisers }
|
|
591
|
+
...params.blockedAdvertisers && { blockedAdvertisers: params.blockedAdvertisers },
|
|
592
|
+
// Developer controls (Phase 2: Revenue Optimization)
|
|
593
|
+
...params.minCPC !== void 0 && { minCPC: params.minCPC },
|
|
594
|
+
...params.minRelevanceScore !== void 0 && { minRelevanceScore: params.minRelevanceScore },
|
|
595
|
+
...params.optimizeFor && { optimizeFor: params.optimizeFor }
|
|
538
596
|
};
|
|
539
597
|
const response = await this.decideRaw(request, options);
|
|
540
598
|
if (response.status === "no_fill" || response.units.length === 0) {
|