mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-13 19:06:24 +00:00
big refactor
* use different kinds per response type * remove examples * remove specific job request definitions, moved to a separate repo for clarity
This commit is contained in:
parent
5026747008
commit
d9400e1e7b
383
90.md
383
90.md
@ -4,22 +4,24 @@ NIP-90
|
||||
Data Vending Machine
|
||||
--------------------
|
||||
|
||||
`draft` `optional` `author:pablof7z`
|
||||
`draft` `optional` `author:pablof7z` `author:dontbelievethehype`
|
||||
|
||||
This NIP defines the interaction between customers and Service Providers for performing on-demand computation.
|
||||
|
||||
Money in, data out.
|
||||
|
||||
## Kinds
|
||||
This NIP reserves the range `65000-66000` for data vending machine use.
|
||||
This NIP reserves the range `5000-7000` for data vending machine use.
|
||||
|
||||
| Kind | Description |
|
||||
| ---- | ----------- |
|
||||
| 65000 | Job feedback |
|
||||
| 65001 | Job result |
|
||||
| 65002-66000 | Job request kinds |
|
||||
| 5000-5999 | Job request kinds |
|
||||
| 6000-6999 | Job result |
|
||||
| 7000 | Job feedback |
|
||||
|
||||
[Appendix 2](#appendix-2-job-types) defines the job request types.
|
||||
Job results always use a kind number that is `1000` higher than the job request kind. (e.g. request: `kind:5001` gets a result: `kind:6001`).
|
||||
|
||||
Job request types are defined [separately](https://github.com/nostr-protocol/data-vending-machines/tree/master/kinds).
|
||||
|
||||
## Rationale
|
||||
Nostr can act as a marketplace for data processing, where users request jobs to be processed in certain ways (e.g., "speech-to-text", "summarization", etc.), but they don't necessarily care about "who" processes the data.
|
||||
@ -31,21 +33,15 @@ There are two actors in the workflow described in this NIP:
|
||||
* Customers (npubs who request a job)
|
||||
* Service providers (npubs who fulfill jobs)
|
||||
|
||||
# Event Kinds
|
||||
|
||||
* `kind:65000`: job feedback
|
||||
* `kind:65001`: job result
|
||||
* `kind:65002`-`kind:66000`: job requests
|
||||
|
||||
## Job request
|
||||
A request to have data processed, published by a customer. This event signals that an npub is interested in receiving the result of some kind of compute.
|
||||
## Job request (`kind:5000-5999`)
|
||||
A request to process data, published by a customer. This event signals that an customer is interested in receiving the result of some kind of compute.
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 65xxx, // kind in 65002-66000 range
|
||||
"kind": 5xxx, // kind in 5000-5999 range
|
||||
"content": "",
|
||||
"tags": [
|
||||
[ "i", "<data>", "<input-type>", "<marker>", "<relay>" ],
|
||||
[ "i", "<data>", "<input-type>", "<relay>", "<marker>" ],
|
||||
[ "output", "<mime-type>" ],
|
||||
[ "relays", "wss://..."],
|
||||
[ "bid", "<msat-amount>" ],
|
||||
@ -59,20 +55,19 @@ All tags are optional.
|
||||
* `i` tag: Input data for the job (zero or more inputs)
|
||||
* `<data>`: The argument for the input
|
||||
* `<input-type>`: The way this argument should be interpreted. MUST be one of:
|
||||
* `url`: A URL to be fetched
|
||||
* `url`: A URL to be fetched of the data that should be processed.
|
||||
* `event`: A Nostr event ID.
|
||||
* `job`: The output of a previous job with the specified event ID
|
||||
* `job`: The output of a previous job with the specified event ID. The dermination of which output to build upon is up to the service provider to decide (e.g. waiting for a signaling from the customer, waiting for a payment, etc.)
|
||||
* `text`: `<data>` is the value of the input, no resolution is needed
|
||||
* `<marker>`: An optional field indicating how this input should be used within the context of the job
|
||||
* `<relay>`: If `event` or `job` input-type, the relay where the event/job was published, otherwise optional or empty string
|
||||
* `output`: Expected output format. (e.g. MIME type)
|
||||
* Service Providers MUST publish the result of the job in this format if it has been specified.
|
||||
* Each job-type ([Appendix 2](#appendix-2-job-types)) might define the output format more narrowly.
|
||||
* `<marker>`: An optional field indicating how this input should be used within the context of the job
|
||||
* `output`: Expected output format. Different job request `kind` defines this more precisely.
|
||||
* `param`: Optional parameters for the job as key (first argument)/value (second argument). Different job request `kind` defines this more precisely. (e.g. `[ "param", "lang", "es" ]`)
|
||||
* `bid`: Customer MAY specify a maximum amount (in millisats) they are willing to pay
|
||||
* `relays`: List of relays where Service Providers SHOULD publish responses to
|
||||
* `p`: Service Providers the customer is interested in. Other SPs MIGHT still choose to process the job
|
||||
|
||||
## Job result
|
||||
## Job result (`kind:6000-6999`)
|
||||
|
||||
Service providers publish job results, providing the output of the job result. They should tag the original job request event id as well as the customer's pubkey.
|
||||
|
||||
@ -80,10 +75,11 @@ Service providers publish job results, providing the output of the job result. T
|
||||
{
|
||||
"pubkey": "<service-provider pubkey>",
|
||||
"content": "<payload>",
|
||||
"kind": 65001,
|
||||
"kind": 6xxx,
|
||||
"tags": [
|
||||
[ "request", "<job-request>" ],
|
||||
[ "e", "<job-request-id>", "<relay-hint>" ],
|
||||
[ "i", "<input-data>" ],
|
||||
[ "p", "<customer's-pubkey>" ],
|
||||
[ "amount", "requested-payment-amount", "<optional-bolt11>" ]
|
||||
]
|
||||
@ -92,13 +88,14 @@ Service providers publish job results, providing the output of the job result. T
|
||||
|
||||
* `request`: The job request event stringified-JSON.
|
||||
* `amount`: millisats that the Service Provider is requesting to be paid. An optional third value can be a bolt11 invoice.
|
||||
* `i`: The original input(s) specified in the request.
|
||||
|
||||
## Job feedback
|
||||
Service providers can give feedback about a job back to the customer.
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 65000,
|
||||
"kind": 7000,
|
||||
"content": "<empty-or-payload>",
|
||||
"tags": [
|
||||
[ "status", "<status>", "<extra-info>" ],
|
||||
@ -110,305 +107,10 @@ Service providers can give feedback about a job back to the customer.
|
||||
```
|
||||
|
||||
* `content`: Either empty or a job-result (e.g. for partial-result samples)
|
||||
* `status` tag: Service Providers SHOULD indicate what this feedback status refers to. [Appendix 3](#appendix-3-job-feedback-status) defines status. Extra human-readable information can be added as an extra argument.
|
||||
* `amount` tag: as defined in the [Job Result](#job-result) section.
|
||||
* `status` tag: Service Providers SHOULD indicate what this feedback status refers to. [Appendix 1](#appendix-1-job-feedback-status) defines status. Extra human-readable information can be added as an extra argument.
|
||||
|
||||
# Protocol Flow
|
||||
* Customer publishes a job request (e.g. `kind:65002` speech-to-text).
|
||||
* Service Providers can submit `kind:65000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
|
||||
* Upon completion, the service provider publishes the result of the job with a `kind:65001` job-result event.
|
||||
* At any point, if there is an `amount` pending to be paid as instructed by the service provider, the user can pay the included `bolt11` or zap the job result event the service provider has sent to the user
|
||||
|
||||
`kind:65000` and `kind:65001` events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers SHOULD use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent. Customers can always either pay the included `bolt11` invoice or zap the event requesting the payment and service providers should monitor for both if they choose to include a bolt11 invoice.
|
||||
|
||||
## Notes about the protocol flow
|
||||
The flow is deliberately ambiguous, allowing vast flexibility for the interaction between customers and service providers so that service providers can model their behavior based on their own decisions/perceptions of risk.
|
||||
|
||||
Some service providers might choose to submit a `payment-required` as the first reaction before sending a `processing` or before delivering `kind:65001` results, some might choose to serve partial results for the job (e.g. as a sample), send a `payment-required` to deliver the rest of the results, and some service providers might choose to assess likelihood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.
|
||||
|
||||
It's not up to this NIP to define how individual vending machines should choose to run their business.
|
||||
|
||||
# Cancellation
|
||||
A job request might be cancelled by publishing a `kind:5` delete request event tagging the job request event.
|
||||
|
||||
# Job chaining
|
||||
A Customer MAY request multiple jobs to be processed as a chain, where the output of a job can be the input of another job. (e.g. podcast transcription -> summarization of the transcription). This is done by specifying as input an event id of a different job with the `job` type.
|
||||
|
||||
Service Providers MAY begin processing a subsequent job the moment they see the prior job's result, but they will likely wait for a zap to be published first. This introduces a risk that Service Provider of job #1 might delay publishing the zap event in order to have an advantage. This risk is up to Service Providers to mitigate or to decide whether the service provider of job #1 tends to have good-enough results so as to not wait for an explicit zap to assume the job was accepted.
|
||||
|
||||
This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).
|
||||
|
||||
Consult [Appendix 1: Example](#appendix-1-examples)'s [Summarization of a podcast](#summarization-of-a-podcast)
|
||||
|
||||
### E.g. Payment required (with sample content)
|
||||
```json
|
||||
{
|
||||
"kind": 65000,
|
||||
"content": "This is the transcription service that you",
|
||||
"tags": [
|
||||
[ "e", <job-request-id>, <relay-hint> ],
|
||||
[ "p", <customer-pubkey> ],
|
||||
[ "status", "payment-required" ],
|
||||
[ "amount", "7000" ],
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Appendix 1: Examples
|
||||
|
||||
## Transcript of a podcast from second `900` to `930`.
|
||||
|
||||
### `kind:65002`: Speech-to-text job request
|
||||
```json
|
||||
{
|
||||
"id": "12345",
|
||||
"pubkey": "abcdef",
|
||||
"content": "",
|
||||
"kind": 65002,
|
||||
"tags": [
|
||||
[ "i", "https://bitcoin.review/episode1.mp3", "url" ],
|
||||
[ "params", "range", "900", "930" ],
|
||||
[ "output", "text/plain" ],
|
||||
[ "bid", "50000" ],
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65000`: Job Feedback: request for (partial) payment
|
||||
* The SP is signaling here that it won't start processing until 100 sats are paid
|
||||
```json
|
||||
{
|
||||
"kind": 65000,
|
||||
"content": "",
|
||||
"tags": [
|
||||
["e", "12345"],
|
||||
["p", "abcdef"],
|
||||
["status", "payment-required"],
|
||||
["amount", "100000"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* User zaps 100 sats to the `kind:65000` job-feedback
|
||||
|
||||
### `kind:65001`: Job result + request for remaining payment
|
||||
```json
|
||||
{
|
||||
"content": "blah blah blah",
|
||||
"tags": [
|
||||
["e", "12345"],
|
||||
["p", "abcdef"],
|
||||
["amount", "400000"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summarization of a podcast
|
||||
User publishes two job requests at the same time. A job that transcribes an audio and a job that summarizes the transcription (output of job #1).
|
||||
|
||||
User publishes event #1 and #2 together.
|
||||
|
||||
### `kind:65002`: Job Request #1: speech-to-text
|
||||
```json
|
||||
{
|
||||
"id": "12345",
|
||||
"pubkey": "abcdef",
|
||||
"kind": 65002,
|
||||
"content": "",
|
||||
"tags": [
|
||||
[ "i", "https://bitcoin.review/episode1.mp3", "url" ],
|
||||
[ "output", "text/plain" ],
|
||||
[ "params", "range", "900", "930" ],
|
||||
[ "bid", "100000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65002`: Job Request #2: summarization of job #1's result
|
||||
```json
|
||||
{
|
||||
"id": "12346",
|
||||
"pubkey": "abcdef",
|
||||
"kind": 65003,
|
||||
"content": "",
|
||||
"tags": [
|
||||
[ "i", "12345", "job" ], // input is the output of job with id 12345
|
||||
[ "output", "text/plain" ],
|
||||
[ "params", "length", "3 paragraphs" ],
|
||||
[ "bid", "10000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Translation of a note
|
||||
### `kind:65004`: Job Request #1: translation of an existing note
|
||||
```json
|
||||
{
|
||||
"id": "12346",
|
||||
"pubkey": "abcdef",
|
||||
"content": "",
|
||||
"kind": 65004,
|
||||
"tags": [
|
||||
[ "i", "<hexid>", "event", "wss://relay.nostr.com" ]
|
||||
[ "output", "text/plain" ],
|
||||
[ "params", "lang", "es_AR" ],
|
||||
[ "bid", "5000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65001`: Job result
|
||||
```json
|
||||
{
|
||||
"kind": 65001,
|
||||
"content": "Che, que copado, boludo!",
|
||||
"tags": [
|
||||
["e", "12346"],
|
||||
["p", "abcdef"],
|
||||
["amount", "4000"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## AI-image of the summarization of 2 podcasts
|
||||
|
||||
### `kind:65002`: Job request #1 (transcribe podcast #1)
|
||||
```json
|
||||
{
|
||||
"id": "123",
|
||||
"kind": 65002,
|
||||
"tags": [
|
||||
[ "i", "https://example.com/episode1.mp3", "url" ],
|
||||
[ "bid", "100000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65002`: Job request #2 (transcribe podcast #2)
|
||||
```json
|
||||
{
|
||||
"id": "124",
|
||||
"kind": 65002,
|
||||
"tags": [
|
||||
[ "i", "https://example.com/episode2.mp3", "url" ],
|
||||
[ "bid", "100000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65003`: Job request #3 (summarize the two job's outputs into one paragraph)
|
||||
```json
|
||||
{
|
||||
"id": "125",
|
||||
"kind": 65003,
|
||||
"tags": [
|
||||
[ "param", "length", "1 paragraph" ],
|
||||
[ "i", "123", "job" ],
|
||||
[ "i", "124", "job" ],
|
||||
[ "bid", "100000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `kind:65005`: Job request #4 (generate image based on the summary)
|
||||
```json
|
||||
{
|
||||
"id": "126",
|
||||
"kind": 65004,
|
||||
"tags": [
|
||||
[ "i", "125", "job" ],
|
||||
[ "param", "prompt", "photorealistic" ],
|
||||
[ "param", "size", "4000x4000" ],
|
||||
[ "bid", "500000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## AI-image of embedded input
|
||||
|
||||
### `kind:65005`: Job request
|
||||
```json
|
||||
{
|
||||
"kind": 65004,
|
||||
"tags": [
|
||||
[ "i", "Millions of vending machines, interconnected with tubes with eah other", "text" ],
|
||||
[ "param", "prompt", "photorealistic" ],
|
||||
[ "bid", "500000" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Appendix 2: Job types
|
||||
|
||||
This is a list of all the supported job requests.
|
||||
|
||||
## speech-to-text: `kind:65002`
|
||||
|
||||
### params
|
||||
|
||||
| param | req? | description |
|
||||
|------------|------|-----------------------------------------------------------|
|
||||
| `range` | opt | timestamp range (in seconds) of desired text to be transcribed |
|
||||
| `alignment`| opt | word, segment, raw: word-level, segment-level, or raw outputs |
|
||||
|
||||
## summarization: `kind:65003`
|
||||
|
||||
| param | req? | description |
|
||||
|-----------|------|---------------|
|
||||
| `length` | opt | desired length |
|
||||
|
||||
## translation: `kind:65004`
|
||||
|
||||
| param | req? | description |
|
||||
|-----------|------|--------------------------------------------|
|
||||
| `lang` | req | desired language in BCP 47 format. |
|
||||
|
||||
## image generation: `kind:65005`
|
||||
|
||||
| param | req? | description |
|
||||
|-----------|------|-------------------------------------------------------|
|
||||
| `prompt` | opt | extra prompt to be used for the image generation |
|
||||
| `size` | opt | desired size of the image |
|
||||
|
||||
## event list generation: `kind:65006`
|
||||
|
||||
Generates a list of event ids, (e.g. algorithmic feeds, spam-free notifications, trending topics)
|
||||
|
||||
Output should be a stringified array of elements usually find in a nostr event' `tags`, e.g.:
|
||||
|
||||
```json
|
||||
{ "content": "[
|
||||
[\"e\", \"<id>\"],
|
||||
[\"a\", \"30023:pubkey:id\"],
|
||||
[\"t\", \"tag\"],
|
||||
[\"p\", \"pubkey\"],
|
||||
]" }
|
||||
```
|
||||
|
||||
| param | req? | description |
|
||||
|-----------|------|-------------------------------------------------------|
|
||||
| `filter` | opt | Stringified JSON `REQ` filter. Used to scope the desired results (e.g. specify the desired kinds)
|
||||
| `prompt` | opt | A human-readable description of the desired results. Which might be used with e.g. an LLM to tune the results.
|
||||
| `p` | opt | Array of pubkeys to generate a feed from someone else's point-of-view. This param allows for a client to choose to generate the feeds and incur the costs of its users.
|
||||
|
||||
### example job-request
|
||||
|
||||
Generate an algorithmic feed of the most interesting `kind:1`s related to the topic "bitcoin", tagging service providers specializing in safe-for-work content
|
||||
that would interest pubkey `pubkey1`.
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 65006,
|
||||
"tags": [
|
||||
[ "param", "filter", "{ \"kinds\": [1], \"#t\": [\"bitcoin\"] }" ],
|
||||
[ "param", "p", "[\"pubkey1\"]"]
|
||||
[ "bid", "5000" ],
|
||||
[ "t", "sfw" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Appendix 3: Job feedback status
|
||||
### Job feedback status
|
||||
|
||||
| status | description |
|
||||
|--------|-------------|
|
||||
@ -420,16 +122,47 @@ that would interest pubkey `pubkey1`.
|
||||
|
||||
Any job feedback event MIGHT include results in the `.content` field, as described in the [Job Result](#job-result) section. This is useful for service providers to provide a sample of the results that have been processed so far.
|
||||
|
||||
# Appendix 4: Service provider discoverability
|
||||
|
||||
Service Providers can use NIP-89 announcements to advertise their support for job kinds:
|
||||
# Protocol Flow
|
||||
* Customer publishes a job request (e.g. `kind:5000` speech-to-text).
|
||||
* Service Providers MAY submit `kind:7000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
|
||||
* Upon completion, the service provider publishes the result of the job with a `kind:6000` job-result event.
|
||||
* At any point, if there is an `amount` pending to be paid as instructed by the service provider, the user can pay the included `bolt11` or zap the job result event the service provider has sent to the user
|
||||
|
||||
Job feedback (`kind:7000`) and Job Results (`kind:6000-6999`) events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers MUST use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent.
|
||||
|
||||
Customers can always either pay the included `bolt11` invoice or zap the event requesting the payment and service providers should monitor for both if they choose to include a bolt11 invoice.
|
||||
|
||||
## Notes about the protocol flow
|
||||
The flow is deliberately ambiguous, allowing vast flexibility for the interaction between customers and service providers so that service providers can model their behavior based on their own decisions/perceptions of risk.
|
||||
|
||||
Some service providers might choose to submit a `payment-required` as the first reaction before sending a `processing` or before delivering results, some might choose to serve partial results for the job (e.g. a sample), send a `payment-required` to deliver the rest of the results, and some service providers might choose to assess likelihood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.
|
||||
|
||||
It's not up to this NIP to define how individual vending machines should choose to run their business.
|
||||
|
||||
# Cancellation
|
||||
A job request might be cancelled by publishing a `kind:5` delete request event tagging the job request event.
|
||||
|
||||
# Appendix 1: Job chaining
|
||||
A Customer MAY request multiple jobs to be processed as a chain, where the output of a job is the input of another job. (e.g. podcast transcription -> summarization of the transcription). This is done by specifying as input an event id of a different job with the `job` type.
|
||||
|
||||
Service Providers MAY begin processing a subsequent job the moment they see the prior job's result, but they will likely wait for a zap to be published first. This introduces a risk that Service Provider of job #1 might delay publishing the zap event in order to have an advantage. This risk is up to Service Providers to mitigate or to decide whether the service provider of job #1 tends to have good-enough results so as to not wait for an explicit zap to assume the job was accepted.
|
||||
|
||||
This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).
|
||||
|
||||
# Appendix 2: Service provider discoverability
|
||||
Service Providers MAY use NIP-89 announcements to advertise their support for job kinds:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 31990,
|
||||
"pubkey": "<pubkey>",
|
||||
"content": "{
|
||||
\"name\": \"Translating DVM\",
|
||||
\"about\": \"I'm a DVM specialized in translating Bitcoin content.\"
|
||||
}",
|
||||
"tags": [
|
||||
[ "k", 65002 ], // e.g. speech-to-text
|
||||
[ "k", 5005 ], // e.g. translation
|
||||
[ "t", "bitcoin" ] // e.g. optionally advertises it specializes in bitcoin audio transcription that won't confuse "Drivechains" with "Ridechains"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user