wip, part 2

This commit is contained in:
pablof7z 2023-07-05 11:14:50 +02:00
parent 71803c21a6
commit ce552554a0

View File

@ -1,8 +1,15 @@
# NIP-XX: Data Vending Machine NIP-XX
Money in, data out. ======
Data Vending Machine
--------------------
`draft` `optional` `author:pablof7z`
This NIP defines the interaction between customers and Service Providers to perform on-demand computation.
## Rationale ## 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"), but where they don't necessarily care about "who" processes the data. 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 where they don't necessarily care about "who" processes the data.
This NIP is not to be confused with a 1:1 marketplace; but rather, a flow where user announces a desired output, willigness to pay, and service providers compete to fulfill the job requirement in the best way possible. This NIP is not to be confused with a 1:1 marketplace; but rather, a flow where user announces a desired output, willigness to pay, and service providers compete to fulfill the job requirement in the best way possible.
@ -11,26 +18,9 @@ There are two actors to the workflow described in this NIP:
* Customers (npubs who request a job) * Customers (npubs who request a job)
* Service providers (npubs who fulfill jobs) * Service providers (npubs who fulfill jobs)
## User flow # Event Kinds
* User publishes a job request ## Job request
`{ "kind": 68001, "tags": [ [ "j", "speech-to-text" ], ... ] }` A request to have data processed -- published by a customer
* Service providers listen for the type of jobs they can perform
`{"kinds":[68001], "#j": ["speech-to-text", "image-generation", ... ]}`
* When a job comes in, the service providers who opt to attempt to fulfill the request begin processing it
* Upon completion, the service provider publishes the result of the job with a `job-result` event.
* Upon acceptance, the user zaps the service provider, tagging the job request
## Kinds
This NIP introduces two new kinds:
* `kind:68001`: Job request -- a request to have a job be processed
* `kind:68002`: Job result -- a proposal of the resulting job
### Job request
A request to have data processed -- published by a user
```json ```json
{ {
@ -38,40 +28,35 @@ A request to have data processed -- published by a user
"content": "", "content": "",
"tags": [ "tags": [
// The type data processing the user wants to be performed // The type data processing the user wants to be performed
// on the [ "j", "<job-type>", "<optional-model>" ],
[ "j", "<job-type>", "<model>" ],
[ "input", "<data>", "<input-type>", "<marker>" ], // input(s) for the job request
[ "i", "<data>", "<input-type>", "<marker>" ],
// relays where the job result should be published
[ "relays", "wss://..."], [ "relays", "wss://..."],
// stringified sat amount that the user is offering to pay // millisats amount that the user is offering to pay
// for this request [ "bid", "<msat-amount>", "<optional-max-price>" ],
// should this include an optional max price or is it too [ "exp", "<timestamp>" ],
// ambiguous?
[ "bid", "<sat-amount>", ["<optional-max-price>"] ],
// max timestamp at which the job is no longer to be processed
[ "expiration", "<timestamp>" ]
// p tags
[ "p", "service-provider-1" ], [ "p", "service-provider-1" ],
[ "p", "service-provider-2" ], [ "p", "service-provider-2" ],
// NIP-33 d-tag
[ "d", "<unique-job-name>"]
] ]
} }
``` ```
#### `content` field ### `content` field
An optional, human-readable description of what this job is for. An optional, human-readable description of what this job is for.
#### `j` tag ### `j` tag
Specifies the job to be executed. A job request MUST have exactly one `j` tag. Specifies the job to be executed. A job request MUST have exactly one `j` tag.
A `j` tag MIGHT name a specific model to be used for the computed with. A `j` tag MIGHT name a specific model to be used for the computed with as the second value.
#### `input` tag ### `i` (input) tag
Specified the input that the job should be executed with. Specifies the input that the job should be executed with. The input is relay-indexable so that clients interested in the exact same job can find it it's result if it's already fulfilled.
A job request CAN have zero or more inputs.
* `<data>`: The argument for the input * `<data>`: The argument for the input
* `<input-type>`: The way this argument should be interpreted * `<input-type>`: The way this argument should be interpreted
@ -81,17 +66,20 @@ Specified the input that the job should be executed with.
* `job`: the output of a previous job with the specified event ID * `job`: the output of a previous job with the specified event ID
* `<marker>`: * `<marker>`:
#### `relays` tag ### `bid` tag
The user MIGHT specify an amount of millisats they are willing to pay for the job to be processed. The user MIGHT also specify a maximum amount of millisats they are willing to pay.
### `relays` tag
A list of relays the service provider should publish its job result to. A list of relays the service provider should publish its job result to.
#### `p` tags (optional) ### `p` tags
A user might want to explicitly request this job to be processed by specific service provider(s). Other service providers might still choose to compete for this job. A user MIGHT want to explicitly request this job to be processed by specific service provider(s). Other service providers might still choose to compete for this job.
#### `expiration` (optional) ### `exp`
A user might specify that they will not be interested in results past a certain time (e.g. a time-sensitive job whos value is no longer relevant after some time, like a live transcription service) A user might specify that they will not be interested in results past a certain time (e.g. a time-sensitive job whos value is no longer relevant after some time, like a live transcription service)
### Job result ## Job result
The output of processing the data -- published by the The output of processing the data -- published by the service provider.
```json ```json
{ {
"pubkey": "service-provider", "pubkey": "service-provider",
@ -100,19 +88,22 @@ The output of processing the data -- published by the
"content": "<payload>", "content": "<payload>",
"tags" [ "tags" [
// stringified JSON request event // stringified JSON request event
[ "request", "<2xxx1-event>" ], [ "request", "<68001-event>" ],
[ "e", <id-of-2xxx1-event>], [ "e", <id-of-68001-event>],
[ "p", "<job-requester's pubkey>" ], [ "p", "<job-requester's pubkey>" ],
[ "status", "success", "<more-info>"], [ "status", "success", "<more-info>"],
[ "payment", "requested-payment-amount" ] [ "amount", "requested-payment-amount" ]
] ]
} }
``` ```
### `status` tag The result of the job should be in the `content`. If the output is not text, the `content` field should be empty and an `output` tag should be used instead as described below.
#### `status` tag
The service provider might want to return an error to the user in case the job could not be processed correctly The service provider might want to return an error to the user in case the job could not be processed correctly
### `payment` #### `amount`
The amount of millisats the service provider is requesting to be paid. This amount MIGHT be different than the amount specified by the user in the `bid` tag. The amount SHOULD be less than the maximum amount specified by the user in the `bid` tag.
## Job types ## Job types
@ -133,21 +124,52 @@ This NIP defines some job types, clients SHOULD specify these types for maximum
#### params #### params
| param | req? | description | param | req? | description
|--------------------------------|------|-------- |--------------------------------|------|--------
| `language` | req | desired language in BCP 47 format. | `language` | req | requested language in BCP 47 format.
## Job chaining # Protocol Flow
* User publishes a job request
`{ "kind": 68001, "tags": [ [ "j", "speech-to-text" ], ... ] }`
* Service providers listen for the type of jobs they can perform
`{"kinds":[68001], "#j": ["speech-to-text", "image-generation", ... ]}`
* When a job comes in, the service providers who opt to attempt to fulfill the request begin processing it, or they can react to it with feedback for the user (e.g. _payment required_, _unprocessable entity_, etc.)
* Upon completion, the service provider publishes the result of the job with a `job-result` event.
* Upon acceptance, the user zaps the service provider, tagging the job result event.
# Payment
Customers SHOULD pay service providers whose job results they accept. Users should zap the service provider, tagging the `kind:68002` job result.
# Job chaining
A customer CAN request multiple jobs to be chained, so that the output of a job can be the input of the next job. (e.g. summarization of a podcast's transcription). This is done by specifying as `input` an eventID of a different job with the `job` marker. A customer CAN request multiple jobs to be chained, so that the output of a job can be the input of the next job. (e.g. summarization of a podcast's transcription). This is done by specifying as `input` an eventID of a different job with the `job` marker.
Service providers might opt to start processing a subsequent job the moment they see the prior job's result, or they might choose to wait for a zap to have been published. This introduces the 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 a explicit zap to assume the job was accepted. Service providers might opt to start processing a subsequent job the moment they see the prior job's result, or they might choose to wait for a zap to have been published. This introduces the 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 a explicit zap to assume the job was accepted.
## Job feedback # Reactions
> **Warning** > **Warning**
> Is this hijacking/modifying the meaning of NIP-25 reactions too much? > Is this hijacking/modifying the meaning of NIP-25 reactions too much?
A user might choose to not accept a job result for any reason. A user can provide feedback via NIP-25 reactions. ## Job request reactions
The `content` of the `kind:7` event SHOULD include a description of how the user reacted to the job result, either Service Providers might opt to give feedback about a job.
in the form of
### E.g. Payment required
```json
{
"kind": 7,
"content": "Please pay 7 sats for job xxxx",
"tags": [
[ "e", <job-request-id> ],
[ "status", "payment-required" ],
[ "amount", "7000" ],
]
}
```
## Job feedback
A user might choose to not accept a job result for any reason. A user can provide feedback via NIP-25 reactions.
The `content` of the `kind:7` event SHOULD include a description of how the user reacted to the job result.
## Explicitly not addressed in this NIP ## Explicitly not addressed in this NIP
@ -163,7 +185,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar
# Appendix 1: Examples # Appendix 1: Examples
## Customer wants to get a transcript of a podcast from second 900 to 930. ## Transcript of a podcast from second `900` to `930`.
### `kind:68001`: Job Request ### `kind:68001`: Job Request
```json ```json
@ -174,7 +196,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar
"tags": [ "tags": [
[ "j", "speech-to-text" ], [ "j", "speech-to-text" ],
[ "params", "range", "900", "930" ], [ "params", "range", "900", "930" ],
[ "input", "https://bitcoin.review/episode1.mp3", "url" ], [ "i", "https://bitcoin.review/episode1.mp3", "url" ],
[ "bid", "5000", "9000" ] [ "bid", "5000", "9000" ]
] ]
} }
@ -192,7 +214,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar
} }
``` ```
## Customer wants to get a summarization of a podcast ## Summarization of a podcast
User publishes two job requests at the same time in the order they should be executed. User publishes two job requests at the same time in the order they should be executed.
@ -205,7 +227,7 @@ User publishes two job requests at the same time in the order they should be exe
"tags": [ "tags": [
[ "j", "speech-to-text" ], [ "j", "speech-to-text" ],
[ "params", "range", "900", "930" ], [ "params", "range", "900", "930" ],
[ "input", "https://bitcoin.review/episode1.mp3", "url" ], [ "i", "https://bitcoin.review/episode1.mp3", "url" ],
[ "bid", "5000", "9000" ] [ "bid", "5000", "9000" ]
] ]
} }
@ -220,13 +242,13 @@ User publishes two job requests at the same time in the order they should be exe
"tags": [ "tags": [
[ "j", "summarization" ], [ "j", "summarization" ],
[ "params", "length", "3 paragraphs" ], [ "params", "length", "3 paragraphs" ],
[ "input", "12346", "job" ], [ "i", "12346", "job" ],
[ "bid", "300", "900" ] [ "bid", "300", "900" ]
] ]
} }
``` ```
## Customer wants a translation of a note ## Translation of a note
### `kind:68001`: Job Request #1 ### `kind:68001`: Job Request #1
```json ```json
{ {
@ -235,9 +257,57 @@ User publishes two job requests at the same time in the order they should be exe
"content": "", "content": "",
"tags": [ "tags": [
[ "j", "translation" ], [ "j", "translation" ],
[ "input", "<hexid>", "event" ] [ "i", "<hexid>", "event" ]
[ "params", "language", "es_AR" ], [ "params", "language", "es_AR" ],
[ "bid", "100", "500" ] [ "bid", "100", "500" ]
] ]
} }
``` ```
## AI-image of the summarization of 2 podcasts
### `kind:68001`: Job request #1 (transcribe podcast #1)
```json
{
"id": "123",
"tags": [
[ "j", "speech-to-text" ],
[ "i", "https://example.com/episode1.mp3", "url" ],
[ "bid", "100", "500" ]
]
}
```
### `kind:68001`: Job request #2 (transcribe podcast #2)
```json
{
"id": "124",
"tags": [
[ "j", "speech-to-text" ],
[ "i", "https://example.com/episode2.mp3", "url" ],
[ "bid", "100", "500" ]
]
}
```
### `kind:68001`: Job request #3 (summarize both podcasts into one podcast)
```json
{
"id": "125",
"tags": [
[ "j", "summarize" ],
[ "param", "length", "1 paragraph" ],
[ "i", "123", "job" ],
[ "i", "124", "job" ],
[ "bid", "100", "500" ]
]
}
```
# Notes
* Should there be a possibility of getting the job result delivered encrypted? I don't like it but maybe it should be supported.
* Ambiguity on job acceptance, particularly for job-chaining circumstances is deliberately ambiguous: service providers could wait until explicit job result acceptance / payment to start working on the next item on the chain, or they could start working as soon as they see a result of the previous job computed.
That's up to each service provider to choose how to behave depending on the circumstances. This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).