Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ For anything not covered here, open an [issue](https://github.com/parallel-web/p

## Submitting a Recipe

A recipe is a self-contained working application with a live demo and a clear pedagogical point. Every recipe in the cookbook should answer *"why would someone copy this?"* in one sentence.
A recipe is a self-contained working example with a clear pedagogical point. Every recipe in the cookbook should answer *"why would someone copy this?"* in one sentence. Hosted applications should include a live demo or deploy path. Local scripts and notebooks can omit a demo when the local workflow is the point and the README includes an end-to-end verification path.

### What makes a good recipe

| ✅ Good | ❌ Avoid |
| --- | --- |
| Solves one specific problem (e.g. "stream Task progress to a UI") | Tries to demo every Parallel API at once (templates are the exception) |
| Shipped with a live demo URL | Local-only with no deploy story |
| Has a one-click deploy button or `DEPLOY.md` | Requires hand-rolling infra to try it |
| Has a live demo, deploy path, or verified local workflow | Uses a fake demo link or leaves the reader without a way to test it |
| Documents deployment when hosting is part of the recipe | Requires infrastructure that the README does not explain |
| Real research/data domain (companies, papers, news) | "Hello world" toy data |
| README explains the *design decisions*, not just setup | README is just `npm install && npm run dev` |
| Uses our latest API surfaces (Search, Extract, Task SSE, Ingest) | Wraps deprecated endpoints |
Expand Down Expand Up @@ -56,7 +56,7 @@ Every recipe README should hit these sections, roughly in this order:
```markdown
# <Recipe Title>

One-sentence pitch. Live demo: <url>
One-sentence pitch. Live demo: <url, when available>

[Deploy button or link to DEPLOY.md]

Expand Down Expand Up @@ -104,6 +104,8 @@ After your recipe is in place, add an entry to two places so it shows up everywh
}
```

Use `null` for `websiteUrl` when an accepted local recipe has no hosted demo. The Cookbook hides the Demo button for those entries.

Reviewers set `featured` / `popular`.

### Tags
Expand All @@ -121,7 +123,7 @@ Propose new tags in your PR if none of these fit.
Before opening a PR, double-check:

- [ ] Recipe runs end-to-end from a fresh clone using only the README
- [ ] Live demo URL is reachable and shows what the README claims
- [ ] Live demo URL is reachable and shows what the README claims, when the recipe has one
- [ ] No API keys, secrets, or `.env` / `.dev.vars` files committed (use `.example` variants)
- [ ] No hardcoded Cloudflare account IDs or KV namespace IDs — use `<YOUR_KV_NAMESPACE_ID>` placeholders and document `wrangler kv:namespace create` in the README
- [ ] Production-only routes live under `env.production` in `wrangler.jsonc`, not the default config
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

---

The Parallel Cookbook is a curated set of recipes that show how to build real applications on Parallel's web research stack — **Search**, **Extract**, **Task**, **Ingest**, and **MCP**. Each recipe is a working app with a live demo, a deploy button, and prose that explains the design decisions.
The Parallel Cookbook is a curated set of recipes that show how to build with Parallel's web research stack — **Search**, **Extract**, **Task**, **Ingest**, and **MCP**. Each recipe is a working example with a clear way to run or deploy it and prose that explains the design decisions.

> **New here?** Start with the [Vercel Template](typescript-recipes/parallel-vercel-template) (TypeScript) or the [Deep Research notebook](python-recipes/Deep_Research_Recipe.ipynb) (Python) for an end-to-end tour of the platform.

Expand Down Expand Up @@ -115,6 +115,7 @@ Recurring research, cron jobs, and webhook delivery.
| Recipe | Description | APIs | Stack | Demo |
| --- | --- | --- | --- | --- |
| [**Daily Insights**](typescript-recipes/parallel-daily-insights) | Cron-triggered daily research feed — runs Tasks on a schedule, persists to KV, publishes a public data feed. Includes a `SPEC.md` showing the task spec used. | `Task` `Webhooks` `Cron` | Cloudflare Workers · KV | – |
| [**Vendor Intelligence**](typescript-recipes/parallel-vendor-intelligence) | Researches vendor risk, watches the structured report for changes, and runs follow-up research only when a change crosses the review threshold. | `Task` `Deep Research` `Monitors` | TypeScript · Local scripts | – |

### Deep Research & Notebooks

Expand Down
5 changes: 5 additions & 0 deletions typescript-recipes/parallel-vendor-intelligence/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PARALLEL_API_KEY=

# Optional policy controls used by the three lifecycle commands.
MONITOR_FREQUENCY=1d
FOLLOW_UP_RISK_THRESHOLD=HIGH
3 changes: 3 additions & 0 deletions typescript-recipes/parallel-vendor-intelligence/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
.env
.vendor-intelligence/
21 changes: 21 additions & 0 deletions typescript-recipes/parallel-vendor-intelligence/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Parallel Web Systems

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
243 changes: 243 additions & 0 deletions typescript-recipes/parallel-vendor-intelligence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# Vendor intelligence with Parallel

Research a vendor once, monitor the same risk report for changes, and investigate only the changes that cross your review threshold.

![Vendor intelligence flow](docs/vendor-intelligence-flow-infographic.png)

This recipe uses the Parallel Task API and Monitor API through three local commands:

- `bootstrap` researches each vendor and starts a snapshot Monitor.
- `check-updates` reconstructs new Monitor events, assesses risk deterministically, and optionally runs focused follow-up research when a change crosses the threshold.
- `cleanup` cancels the Monitors recorded in this recipe's local state.

Parallel provides the researched evidence. The recipe validates that evidence and applies a deterministic risk policy, so the final risk level and human-review guidance do not depend on another model call.

## Quick start

You need Node.js 20 or newer and a [Parallel API key](https://platform.parallel.ai). `jq` is optional; it only formats the JSON output in the examples below.

```bash
git clone https://github.com/parallel-web/parallel-cookbook.git
cd parallel-cookbook/typescript-recipes/parallel-vendor-intelligence
npm ci
cp .env.example .env
chmod 600 .env
```

Add your key to `.env`:

```dotenv
PARALLEL_API_KEY=your-api-key
```

Keep `.env` private. It is ignored by Git.

The included vendor file contains one public company, Cloudflare. Create its baseline and Monitor:

```bash
npm run --silent bootstrap | jq
```

This command consumes credits. A normal first run can take a minute or two. It prints progress to stderr and one JSON result to stdout, so piping stdout to `jq` keeps the result readable.

A successful result includes the Task and Monitor IDs, the cited report, and the policy decision. The shortened example below uses sample values:

```json
{
"vendors": 1,
"baselinesCreated": 1,
"monitorsCreated": 1,
"results": [
{
"vendor": { "name": "Cloudflare", "domain": "cloudflare.com" },
"baseline": { "action": "created", "runId": "trun_..." },
"monitor": {
"action": "created",
"monitorId": "monitor_...",
"frequency": "1d",
"processor": "lite"
},
"assessment": {
"risk": {
"level": "MEDIUM",
"requiresHumanReview": true,
"guidance": "analyst_review"
}
}
}
]
}
```

Leave the Monitor active if you want it to keep watching the vendor. Run the update check later, after the Monitor has had time to produce an event:

```bash
npm run --silent check-updates | jq
```

When a change is found, the command emits one structured summary. This abridged example uses sample IDs, findings, and citations rather than output from a specific live run:

```json
{
"monitorsChecked": 1,
"newEvents": 1,
"followUpDecisions": 1,
"followUpTasksCreated": 1,
"followUpsCompleted": 1,
"humanReviewsRequired": 1,
"changes": [
{
"vendor": { "name": "Cloudflare", "domain": "cloudflare.com" },
"event": {
"monitorId": "monitor_...",
"eventId": "mevt_...",
"eventDate": "2026-07-14",
"changedFields": ["cybersecurity"]
},
"assessment": {
"risk": {
"level": "HIGH",
"requiresHumanReview": true,
"guidance": "urgent_human_review"
}
},
"decision": {
"runFollowUp": true,
"threshold": "HIGH",
"previousLevel": "MEDIUM",
"currentLevel": "HIGH"
},
"followUp": {
"status": "completed",
"runId": "trun_...",
"investigation": {
"what_changed": "A newly reported security incident changed the assessment.",
"confirmed_facts": ["The incident was confirmed in public reporting."],
"business_impact": "Review exposure and contingency plans.",
"open_questions": ["Does the incident affect shared data?"]
}
}
}
],
"warnings": [],
"errors": []
}
```

An empty `changes` list is a successful result; it means there are no new Monitor events to process. Running `bootstrap` again reuses the completed baseline and matching active Monitor.

When you are finished, cancel every Monitor owned by this recipe:

```bash
npm run --silent cleanup | jq
```

Do not delete `.vendor-intelligence/state.json` before cleanup. The state file tells the recipe which Monitors it owns.

## Use your own vendor list

`bootstrap` reads `examples/vendors.json` by default. Each entry needs a name and domain. `riskFloor` is optional:

```json
[
{
"name": "Cloudflare",
"domain": "cloudflare.com",
"riskFloor": "MEDIUM"
}
]
```

Run the command with another file:

```bash
npm run --silent bootstrap -- --vendors /absolute/path/to/vendors.json | jq
```

The input must contain at least one vendor and cannot contain duplicate domains. Domains are normalized before any API call.

Bootstrap is additive: removing a vendor from the input file does not cancel its existing Monitor. Cancel one vendor explicitly with:

```bash
npm run --silent cleanup -- --vendor cloudflare.com | jq
```

Repeat `--vendor` to cancel several vendors. Run cleanup without flags to cancel all active Monitors recorded in local state.

## How the recipe controls cost

| Stage | Processor | When it runs |
| --- | --- | --- |
| Baseline research | `core` | Once for each new vendor or explicit retry |
| Snapshot monitoring | `lite` | At the configured Monitor frequency |
| Focused investigation | `pro` | Only when a changed risk field meets the review threshold |

A vendor `riskFloor` at or above `FOLLOW_UP_RISK_THRESHOLD` also triggers investigation for any changed risk field. Tasks and active Monitors consume Parallel credits. Completed Tasks remain in your account history and cannot be cancelled.

The risk assessment and threshold decision are completed before any optional follow-up Task. Follow-up research adds confirmed facts, business impact, open questions, and citations; it does not rescore the report or replace the deterministic decision.

`adverse_events` are discrete, evidence-backed events returned by the Task, such as a breach or lawsuit. Their severity participates in aggregate scoring, and the presence of any supported adverse event requires human review even when its severity is `LOW`.

`check-updates` reports each processed event with its changed fields, current assessment, policy decision, and one follow-up status:

- `not_required`: the change did not cross the threshold.
- `pending`: follow-up research is still running.
- `completed`: the result includes confirmed facts, business impact, open questions, and citations.
- `failed`: the result includes the terminal Task ID and error.

## State and recovery

The recipe stores Task IDs, Monitor IDs, evidence snapshots, and processed event IDs in `.vendor-intelligence/state.json`. Writes are validated and atomic. A command lock prevents two lifecycle commands from changing the same state at once. Monitor patches are applied oldest to newest to a known complete predecessor; an invalid event blocks dependent partial patches until a complete event rebases the chain.

Commands are safe to repeat. A repeated command resumes a running Task, reuses a matching Monitor, and skips Monitor events it has already processed. Cleanup only cancels Monitor IDs found in local state.

If a command stops because of a timeout or temporary network error, run the same command again. If a Task reaches a terminal state such as `failed`, `cancelled`, or `action_required`, inspect the error before creating a replacement:

```bash
npm run bootstrap -- --retry-failed
npm run check-updates -- --retry-failed
```

The recipe does not automatically retry a request that creates a paid Task or Monitor. The API cannot safely tell whether a lost response created the resource, so an automatic retry could spend credits twice. If the state file is malformed, the recipe stops instead of silently resetting it. Back up the file and recover any recorded Monitor IDs before removing the state directory.

If a process stops while recovering a stale command lock, the error names the recovery-marker file. First confirm that no recipe command is still running. Then delete that file and run the command again:

```bash
rm -rf .vendor-intelligence/command.lock.reclaim
```

## Customize the policy

Only the API key is required. Two environment variables change the Monitor schedule and review threshold:

```dotenv
MONITOR_FREQUENCY=1d
FOLLOW_UP_RISK_THRESHOLD=HIGH
```

`MONITOR_FREQUENCY` accepts values from `1h` through `30d`. `FOLLOW_UP_RISK_THRESHOLD` accepts `LOW`, `MEDIUM`, `HIGH`, or `CRITICAL`.

The six risk dimensions live in [`src/schema.ts`](src/schema.ts). Aggregate risk and human guidance live in [`src/risk-policy.ts`](src/risk-policy.ts). Change those two modules to adapt the recipe to your organization's vendor policy.

## Test the recipe

The normal test suite makes no API calls:

```bash
npm run validate
npm audit --audit-level=high
```

The live test consumes credits. It researches Cloudflare with a real `core` Task, creates a `lite` snapshot Monitor, checks for updates, cancels the Monitor in cleanup, and confirms its remote status is `cancelled`:

```bash
npm run test:live
```

The Task API may print an advisory warning that the baseline could be too complex for the `core` processor. The command still validates the complete six-dimension report before it succeeds. If your own baseline fails validation or the evidence is not useful, simplify the report in [`src/schema.ts`](src/schema.ts) or change `baselineProcessor` to `pro` in [`src/vendor-config.ts`](src/vendor-config.ts).

The live test does not wait for a real vendor change. Deterministic tests cover the change-to-follow-up path.

## License

[MIT](LICENSE)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"name": "Cloudflare",
"domain": "cloudflare.com"
}
]
Loading
Loading