diff --git a/vending-machine.md b/vending-machine.md index a94cd33..eb26688 100644 --- a/vending-machine.md +++ b/vending-machine.md @@ -1,8 +1,15 @@ -# NIP-XX: Data Vending Machine -Money in, data out. +NIP-XX +====== + +Data Vending Machine +-------------------- + +`draft` `optional` `author:pablof7z` + +This NIP defines the interaction between customers and Service Providers to perform on-demand computation. ## 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. @@ -11,26 +18,9 @@ There are two actors to the workflow described in this NIP: * Customers (npubs who request a job) * Service providers (npubs who fulfill jobs) -## User 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 -* 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 +# Event Kinds +## Job request +A request to have data processed -- published by a customer ```json { @@ -38,40 +28,35 @@ A request to have data processed -- published by a user "content": "", "tags": [ // The type data processing the user wants to be performed - // on the - [ "j", "", "" ], - [ "input", "", "", "" ], + [ "j", "", "" ], + + // input(s) for the job request + [ "i", "", "", "" ], + + // relays where the job result should be published [ "relays", "wss://..."], - // stringified sat amount that the user is offering to pay - // for this request - // should this include an optional max price or is it too - // ambiguous? - [ "bid", "", [""] ], - - // max timestamp at which the job is no longer to be processed - [ "expiration", "" ] - - // p tags + // millisats amount that the user is offering to pay + [ "bid", "", "" ], + [ "exp", "" ], [ "p", "service-provider-1" ], [ "p", "service-provider-2" ], - - // NIP-33 d-tag - [ "d", ""] ] } ``` -#### `content` field +### `content` field 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. -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 -Specified the input that the job should be executed with. +### `i` (input) tag +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. * ``: The argument for the input * ``: 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 * ``: -#### `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. -#### `p` tags (optional) -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. +### `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. -#### `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) -### Job result -The output of processing the data -- published by the +## Job result +The output of processing the data -- published by the service provider. ```json { "pubkey": "service-provider", @@ -100,19 +88,22 @@ The output of processing the data -- published by the "content": "", "tags" [ // stringified JSON request event - [ "request", "<2xxx1-event>" ], - [ "e", ], + [ "request", "<68001-event>" ], + [ "e", ], [ "p", "" ], [ "status", "success", ""], - [ "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 -### `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 @@ -133,21 +124,52 @@ This NIP defines some job types, clients SHOULD specify these types for maximum #### params | 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. 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** > 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. -The `content` of the `kind:7` event SHOULD include a description of how the user reacted to the job result, either -in the form of +## Job request reactions +Service Providers might opt to give feedback about a job. +### E.g. Payment required +```json +{ + "kind": 7, + "content": "Please pay 7 sats for job xxxx", + "tags": [ + [ "e", ], + [ "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 @@ -163,7 +185,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar # 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 ```json @@ -174,7 +196,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar "tags": [ [ "j", "speech-to-text" ], [ "params", "range", "900", "930" ], - [ "input", "https://bitcoin.review/episode1.mp3", "url" ], + [ "i", "https://bitcoin.review/episode1.mp3", "url" ], [ "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. @@ -205,7 +227,7 @@ User publishes two job requests at the same time in the order they should be exe "tags": [ [ "j", "speech-to-text" ], [ "params", "range", "900", "930" ], - [ "input", "https://bitcoin.review/episode1.mp3", "url" ], + [ "i", "https://bitcoin.review/episode1.mp3", "url" ], [ "bid", "5000", "9000" ] ] } @@ -220,13 +242,13 @@ User publishes two job requests at the same time in the order they should be exe "tags": [ [ "j", "summarization" ], [ "params", "length", "3 paragraphs" ], - [ "input", "12346", "job" ], + [ "i", "12346", "job" ], [ "bid", "300", "900" ] ] } ``` -## Customer wants a translation of a note +## Translation of a note ### `kind:68001`: Job Request #1 ```json { @@ -235,9 +257,57 @@ User publishes two job requests at the same time in the order they should be exe "content": "", "tags": [ [ "j", "translation" ], - [ "input", "", "event" ] + [ "i", "", "event" ] [ "params", "language", "es_AR" ], [ "bid", "100", "500" ] ] } -``` \ No newline at end of file +``` + +## 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).