diff options
| author | pablof7z <p@f7z.io> | 2023-07-05 11:14:50 +0200 |
|---|---|---|
| committer | pablof7z <p@f7z.io> | 2023-07-05 11:14:50 +0200 |
| commit | ce552554a08bb5b9878621abfdde3207d54cae62 (patch) | |
| tree | e4ecb88c369f25242045692944bf73bed0a737c0 | |
| parent | 71803c21a6bdd2413244a47752c129e3c64eaec6 (diff) | |
wip, part 2
| -rw-r--r-- | vending-machine.md | 206 |
1 files changed, 138 insertions, 68 deletions
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 @@ | |||
| 1 | # NIP-XX: Data Vending Machine | 1 | NIP-XX |
| 2 | Money in, data out. | 2 | ====== |
| 3 | |||
| 4 | Data Vending Machine | ||
| 5 | -------------------- | ||
| 6 | |||
| 7 | `draft` `optional` `author:pablof7z` | ||
| 8 | |||
| 9 | This NIP defines the interaction between customers and Service Providers to perform on-demand computation. | ||
| 3 | 10 | ||
| 4 | ## Rationale | 11 | ## Rationale |
| 5 | 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. | 12 | 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. |
| 6 | 13 | ||
| 7 | 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. | 14 | 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. |
| 8 | 15 | ||
| @@ -11,26 +18,9 @@ There are two actors to the workflow described in this NIP: | |||
| 11 | * Customers (npubs who request a job) | 18 | * Customers (npubs who request a job) |
| 12 | * Service providers (npubs who fulfill jobs) | 19 | * Service providers (npubs who fulfill jobs) |
| 13 | 20 | ||
| 14 | ## User flow | 21 | # Event Kinds |
| 15 | * User publishes a job request | 22 | ## Job request |
| 16 | `{ "kind": 68001, "tags": [ [ "j", "speech-to-text" ], ... ] }` | 23 | A request to have data processed -- published by a customer |
| 17 | |||
| 18 | * Service providers listen for the type of jobs they can perform | ||
| 19 | `{"kinds":[68001], "#j": ["speech-to-text", "image-generation", ... ]}` | ||
| 20 | |||
| 21 | * When a job comes in, the service providers who opt to attempt to fulfill the request begin processing it | ||
| 22 | * Upon completion, the service provider publishes the result of the job with a `job-result` event. | ||
| 23 | * Upon acceptance, the user zaps the service provider, tagging the job request | ||
| 24 | |||
| 25 | ## Kinds | ||
| 26 | |||
| 27 | This NIP introduces two new kinds: | ||
| 28 | |||
| 29 | * `kind:68001`: Job request -- a request to have a job be processed | ||
| 30 | * `kind:68002`: Job result -- a proposal of the resulting job | ||
| 31 | |||
| 32 | ### Job request | ||
| 33 | A request to have data processed -- published by a user | ||
| 34 | 24 | ||
| 35 | ```json | 25 | ```json |
| 36 | { | 26 | { |
| @@ -38,40 +28,35 @@ A request to have data processed -- published by a user | |||
| 38 | "content": "", | 28 | "content": "", |
| 39 | "tags": [ | 29 | "tags": [ |
| 40 | // The type data processing the user wants to be performed | 30 | // The type data processing the user wants to be performed |
| 41 | // on the | 31 | [ "j", "<job-type>", "<optional-model>" ], |
| 42 | [ "j", "<job-type>", "<model>" ], | ||
| 43 | [ "input", "<data>", "<input-type>", "<marker>" ], | ||
| 44 | [ "relays", "wss://..."], | ||
| 45 | 32 | ||
| 46 | // stringified sat amount that the user is offering to pay | 33 | // input(s) for the job request |
| 47 | // for this request | 34 | [ "i", "<data>", "<input-type>", "<marker>" ], |
| 48 | // should this include an optional max price or is it too | ||
| 49 | // ambiguous? | ||
| 50 | [ "bid", "<sat-amount>", ["<optional-max-price>"] ], | ||
| 51 | 35 | ||
| 52 | // max timestamp at which the job is no longer to be processed | 36 | // relays where the job result should be published |
| 53 | [ "expiration", "<timestamp>" ] | 37 | [ "relays", "wss://..."], |
| 54 | 38 | ||
| 55 | // p tags | 39 | // millisats amount that the user is offering to pay |
| 40 | [ "bid", "<msat-amount>", "<optional-max-price>" ], | ||
| 41 | [ "exp", "<timestamp>" ], | ||
| 56 | [ "p", "service-provider-1" ], | 42 | [ "p", "service-provider-1" ], |
| 57 | [ "p", "service-provider-2" ], | 43 | [ "p", "service-provider-2" ], |
| 58 | |||
| 59 | // NIP-33 d-tag | ||
| 60 | [ "d", "<unique-job-name>"] | ||
| 61 | ] | 44 | ] |
| 62 | } | 45 | } |
| 63 | ``` | 46 | ``` |
| 64 | 47 | ||
| 65 | #### `content` field | 48 | ### `content` field |
| 66 | An optional, human-readable description of what this job is for. | 49 | An optional, human-readable description of what this job is for. |
| 67 | 50 | ||
| 68 | #### `j` tag | 51 | ### `j` tag |
| 69 | Specifies the job to be executed. A job request MUST have exactly one `j` tag. | 52 | Specifies the job to be executed. A job request MUST have exactly one `j` tag. |
| 70 | 53 | ||
| 71 | A `j` tag MIGHT name a specific model to be used for the computed with. | 54 | A `j` tag MIGHT name a specific model to be used for the computed with as the second value. |
| 72 | 55 | ||
| 73 | #### `input` tag | 56 | ### `i` (input) tag |
| 74 | Specified the input that the job should be executed with. | 57 | 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. |
| 58 | |||
| 59 | A job request CAN have zero or more inputs. | ||
| 75 | 60 | ||
| 76 | * `<data>`: The argument for the input | 61 | * `<data>`: The argument for the input |
| 77 | * `<input-type>`: The way this argument should be interpreted | 62 | * `<input-type>`: The way this argument should be interpreted |
| @@ -81,17 +66,20 @@ Specified the input that the job should be executed with. | |||
| 81 | * `job`: the output of a previous job with the specified event ID | 66 | * `job`: the output of a previous job with the specified event ID |
| 82 | * `<marker>`: | 67 | * `<marker>`: |
| 83 | 68 | ||
| 84 | #### `relays` tag | 69 | ### `bid` tag |
| 70 | 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. | ||
| 71 | |||
| 72 | ### `relays` tag | ||
| 85 | A list of relays the service provider should publish its job result to. | 73 | A list of relays the service provider should publish its job result to. |
| 86 | 74 | ||
| 87 | #### `p` tags (optional) | 75 | ### `p` tags |
| 88 | 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. | 76 | 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. |
| 89 | 77 | ||
| 90 | #### `expiration` (optional) | 78 | ### `exp` |
| 91 | 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) | 79 | 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) |
| 92 | 80 | ||
| 93 | ### Job result | 81 | ## Job result |
| 94 | The output of processing the data -- published by the | 82 | The output of processing the data -- published by the service provider. |
| 95 | ```json | 83 | ```json |
| 96 | { | 84 | { |
| 97 | "pubkey": "service-provider", | 85 | "pubkey": "service-provider", |
| @@ -100,19 +88,22 @@ The output of processing the data -- published by the | |||
| 100 | "content": "<payload>", | 88 | "content": "<payload>", |
| 101 | "tags" [ | 89 | "tags" [ |
| 102 | // stringified JSON request event | 90 | // stringified JSON request event |
| 103 | [ "request", "<2xxx1-event>" ], | 91 | [ "request", "<68001-event>" ], |
| 104 | [ "e", <id-of-2xxx1-event>], | 92 | [ "e", <id-of-68001-event>], |
| 105 | [ "p", "<job-requester's pubkey>" ], | 93 | [ "p", "<job-requester's pubkey>" ], |
| 106 | [ "status", "success", "<more-info>"], | 94 | [ "status", "success", "<more-info>"], |
| 107 | [ "payment", "requested-payment-amount" ] | 95 | [ "amount", "requested-payment-amount" ] |
| 108 | ] | 96 | ] |
| 109 | } | 97 | } |
| 110 | ``` | 98 | ``` |
| 111 | 99 | ||
| 112 | ### `status` tag | 100 | 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. |
| 101 | |||
| 102 | #### `status` tag | ||
| 113 | The service provider might want to return an error to the user in case the job could not be processed correctly | 103 | The service provider might want to return an error to the user in case the job could not be processed correctly |
| 114 | 104 | ||
| 115 | ### `payment` | 105 | #### `amount` |
| 106 | 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. | ||
| 116 | 107 | ||
| 117 | ## Job types | 108 | ## Job types |
| 118 | 109 | ||
| @@ -133,21 +124,52 @@ This NIP defines some job types, clients SHOULD specify these types for maximum | |||
| 133 | #### params | 124 | #### params |
| 134 | | param | req? | description | 125 | | param | req? | description |
| 135 | |--------------------------------|------|-------- | 126 | |--------------------------------|------|-------- |
| 136 | | `language` | req | desired language in BCP 47 format. | 127 | | `language` | req | requested language in BCP 47 format. |
| 128 | |||
| 129 | # Protocol Flow | ||
| 130 | * User publishes a job request | ||
| 131 | `{ "kind": 68001, "tags": [ [ "j", "speech-to-text" ], ... ] }` | ||
| 132 | |||
| 133 | * Service providers listen for the type of jobs they can perform | ||
| 134 | `{"kinds":[68001], "#j": ["speech-to-text", "image-generation", ... ]}` | ||
| 135 | |||
| 136 | * 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.) | ||
| 137 | * Upon completion, the service provider publishes the result of the job with a `job-result` event. | ||
| 138 | * Upon acceptance, the user zaps the service provider, tagging the job result event. | ||
| 137 | 139 | ||
| 138 | ## Job chaining | 140 | # Payment |
| 141 | Customers SHOULD pay service providers whose job results they accept. Users should zap the service provider, tagging the `kind:68002` job result. | ||
| 142 | |||
| 143 | |||
| 144 | # Job chaining | ||
| 139 | 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. | 145 | 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. |
| 140 | 146 | ||
| 141 | 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. | 147 | 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. |
| 142 | 148 | ||
| 143 | ## Job feedback | 149 | # Reactions |
| 144 | > **Warning** | 150 | > **Warning** |
| 145 | > Is this hijacking/modifying the meaning of NIP-25 reactions too much? | 151 | > Is this hijacking/modifying the meaning of NIP-25 reactions too much? |
| 146 | 152 | ||
| 147 | A user might choose to not accept a job result for any reason. A user can provide feedback via NIP-25 reactions. | 153 | ## Job request reactions |
| 148 | The `content` of the `kind:7` event SHOULD include a description of how the user reacted to the job result, either | 154 | Service Providers might opt to give feedback about a job. |
| 149 | in the form of | 155 | |
| 156 | ### E.g. Payment required | ||
| 157 | ```json | ||
| 158 | { | ||
| 159 | "kind": 7, | ||
| 160 | "content": "Please pay 7 sats for job xxxx", | ||
| 161 | "tags": [ | ||
| 162 | [ "e", <job-request-id> ], | ||
| 163 | [ "status", "payment-required" ], | ||
| 164 | [ "amount", "7000" ], | ||
| 165 | ] | ||
| 166 | } | ||
| 167 | ``` | ||
| 150 | 168 | ||
| 169 | ## Job feedback | ||
| 170 | |||
| 171 | A user might choose to not accept a job result for any reason. A user can provide feedback via NIP-25 reactions. | ||
| 172 | The `content` of the `kind:7` event SHOULD include a description of how the user reacted to the job result. | ||
| 151 | 173 | ||
| 152 | ## Explicitly not addressed in this NIP | 174 | ## Explicitly not addressed in this NIP |
| 153 | 175 | ||
| @@ -163,7 +185,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar | |||
| 163 | 185 | ||
| 164 | # Appendix 1: Examples | 186 | # Appendix 1: Examples |
| 165 | 187 | ||
| 166 | ## Customer wants to get a transcript of a podcast from second 900 to 930. | 188 | ## Transcript of a podcast from second `900` to `930`. |
| 167 | 189 | ||
| 168 | ### `kind:68001`: Job Request | 190 | ### `kind:68001`: Job Request |
| 169 | ```json | 191 | ```json |
| @@ -174,7 +196,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar | |||
| 174 | "tags": [ | 196 | "tags": [ |
| 175 | [ "j", "speech-to-text" ], | 197 | [ "j", "speech-to-text" ], |
| 176 | [ "params", "range", "900", "930" ], | 198 | [ "params", "range", "900", "930" ], |
| 177 | [ "input", "https://bitcoin.review/episode1.mp3", "url" ], | 199 | [ "i", "https://bitcoin.review/episode1.mp3", "url" ], |
| 178 | [ "bid", "5000", "9000" ] | 200 | [ "bid", "5000", "9000" ] |
| 179 | ] | 201 | ] |
| 180 | } | 202 | } |
| @@ -192,7 +214,7 @@ It's out of scope (and undesirable) to have this NIP address this issue; the mar | |||
| 192 | } | 214 | } |
| 193 | ``` | 215 | ``` |
| 194 | 216 | ||
| 195 | ## Customer wants to get a summarization of a podcast | 217 | ## Summarization of a podcast |
| 196 | 218 | ||
| 197 | User publishes two job requests at the same time in the order they should be executed. | 219 | User publishes two job requests at the same time in the order they should be executed. |
| 198 | 220 | ||
| @@ -205,7 +227,7 @@ User publishes two job requests at the same time in the order they should be exe | |||
| 205 | "tags": [ | 227 | "tags": [ |
| 206 | [ "j", "speech-to-text" ], | 228 | [ "j", "speech-to-text" ], |
| 207 | [ "params", "range", "900", "930" ], | 229 | [ "params", "range", "900", "930" ], |
| 208 | [ "input", "https://bitcoin.review/episode1.mp3", "url" ], | 230 | [ "i", "https://bitcoin.review/episode1.mp3", "url" ], |
| 209 | [ "bid", "5000", "9000" ] | 231 | [ "bid", "5000", "9000" ] |
| 210 | ] | 232 | ] |
| 211 | } | 233 | } |
| @@ -220,13 +242,13 @@ User publishes two job requests at the same time in the order they should be exe | |||
| 220 | "tags": [ | 242 | "tags": [ |
| 221 | [ "j", "summarization" ], | 243 | [ "j", "summarization" ], |
| 222 | [ "params", "length", "3 paragraphs" ], | 244 | [ "params", "length", "3 paragraphs" ], |
| 223 | [ "input", "12346", "job" ], | 245 | [ "i", "12346", "job" ], |
| 224 | [ "bid", "300", "900" ] | 246 | [ "bid", "300", "900" ] |
| 225 | ] | 247 | ] |
| 226 | } | 248 | } |
| 227 | ``` | 249 | ``` |
| 228 | 250 | ||
| 229 | ## Customer wants a translation of a note | 251 | ## Translation of a note |
| 230 | ### `kind:68001`: Job Request #1 | 252 | ### `kind:68001`: Job Request #1 |
| 231 | ```json | 253 | ```json |
| 232 | { | 254 | { |
| @@ -235,9 +257,57 @@ User publishes two job requests at the same time in the order they should be exe | |||
| 235 | "content": "", | 257 | "content": "", |
| 236 | "tags": [ | 258 | "tags": [ |
| 237 | [ "j", "translation" ], | 259 | [ "j", "translation" ], |
| 238 | [ "input", "<hexid>", "event" ] | 260 | [ "i", "<hexid>", "event" ] |
| 239 | [ "params", "language", "es_AR" ], | 261 | [ "params", "language", "es_AR" ], |
| 240 | [ "bid", "100", "500" ] | 262 | [ "bid", "100", "500" ] |
| 241 | ] | 263 | ] |
| 242 | } | 264 | } |
| 243 | ``` \ No newline at end of file | 265 | ``` |
| 266 | |||
| 267 | ## AI-image of the summarization of 2 podcasts | ||
| 268 | |||
| 269 | ### `kind:68001`: Job request #1 (transcribe podcast #1) | ||
| 270 | ```json | ||
| 271 | { | ||
| 272 | "id": "123", | ||
| 273 | "tags": [ | ||
| 274 | [ "j", "speech-to-text" ], | ||
| 275 | [ "i", "https://example.com/episode1.mp3", "url" ], | ||
| 276 | [ "bid", "100", "500" ] | ||
| 277 | ] | ||
| 278 | } | ||
| 279 | ``` | ||
| 280 | |||
| 281 | ### `kind:68001`: Job request #2 (transcribe podcast #2) | ||
| 282 | ```json | ||
| 283 | { | ||
| 284 | "id": "124", | ||
| 285 | "tags": [ | ||
| 286 | [ "j", "speech-to-text" ], | ||
| 287 | [ "i", "https://example.com/episode2.mp3", "url" ], | ||
| 288 | [ "bid", "100", "500" ] | ||
| 289 | ] | ||
| 290 | } | ||
| 291 | ``` | ||
| 292 | |||
| 293 | ### `kind:68001`: Job request #3 (summarize both podcasts into one podcast) | ||
| 294 | ```json | ||
| 295 | { | ||
| 296 | "id": "125", | ||
| 297 | "tags": [ | ||
| 298 | [ "j", "summarize" ], | ||
| 299 | [ "param", "length", "1 paragraph" ], | ||
| 300 | [ "i", "123", "job" ], | ||
| 301 | [ "i", "124", "job" ], | ||
| 302 | [ "bid", "100", "500" ] | ||
| 303 | ] | ||
| 304 | } | ||
| 305 | ``` | ||
| 306 | |||
| 307 | # Notes | ||
| 308 | |||
| 309 | * Should there be a possibility of getting the job result delivered encrypted? I don't like it but maybe it should be supported. | ||
| 310 | |||
| 311 | * 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. | ||
| 312 | |||
| 313 | 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). | ||