@output.ai/cli 0.7.11-dev.pr263-8f8e94a → 0.7.12-dev.pr263-5d2eaa9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/README.md +1 -1
  2. package/dist/api/generated/api.d.ts +1 -1
  3. package/dist/api/generated/api.js +1 -1
  4. package/dist/generated/sdk_versions.json +2 -2
  5. package/dist/services/messages.js +2 -2
  6. package/dist/templates/project/README.md.template +3 -3
  7. package/dist/templates/project/src/shared/clients/jina.ts.template +30 -0
  8. package/dist/templates/project/src/shared/utils/url.ts.template +15 -0
  9. package/dist/templates/project/src/workflows/blog_evaluator/evaluators.ts.template +33 -0
  10. package/dist/templates/project/src/workflows/blog_evaluator/prompts/signal_noise@v1.prompt.template +28 -0
  11. package/dist/templates/project/src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json.template +3 -0
  12. package/dist/templates/project/src/workflows/blog_evaluator/steps.ts.template +27 -0
  13. package/dist/templates/project/src/workflows/blog_evaluator/types.ts.template +24 -0
  14. package/dist/templates/project/src/workflows/blog_evaluator/utils.ts.template +12 -0
  15. package/dist/templates/project/src/workflows/blog_evaluator/workflow.ts.template +25 -0
  16. package/package.json +5 -7
  17. package/dist/templates/project/src/shared/clients/pokeapi.ts.template +0 -43
  18. package/dist/templates/project/src/workflows/poke_battle/evaluators.ts.template +0 -47
  19. package/dist/templates/project/src/workflows/poke_battle/prompts/evaluate_realism@v1.prompt.template +0 -26
  20. package/dist/templates/project/src/workflows/poke_battle/prompts/generate_screenplay@v1.prompt.template +0 -20
  21. package/dist/templates/project/src/workflows/poke_battle/scenarios/pikachu_vs_charmander.json.template +0 -4
  22. package/dist/templates/project/src/workflows/poke_battle/steps.ts.template +0 -58
  23. package/dist/templates/project/src/workflows/poke_battle/utils.ts.template +0 -7
  24. package/dist/templates/project/src/workflows/poke_battle/workflow.ts.template +0 -45
package/README.md CHANGED
@@ -18,7 +18,7 @@ cd <project-name>
18
18
  npx output dev
19
19
 
20
20
  # Run a workflow
21
- npx output workflow run example_question --input '{"question": "who is ada lovelace?"}'
21
+ npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json
22
22
  ```
23
23
 
24
24
  ## Environment Configuration
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by orval v7.13.2 🍺
2
+ * Generated by orval v7.20.0 🍺
3
3
  * Do not edit manually.
4
4
  * Output.ai SDK API
5
5
  * API for managing and executing Temporal workflows through Output SDK
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by orval v7.13.2 🍺
2
+ * Generated by orval v7.20.0 🍺
3
3
  * Do not edit manually.
4
4
  * Output.ai SDK API
5
5
  * API for managing and executing Temporal workflows through Output SDK
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "core": "0.3.0",
3
- "llm": "0.2.8",
3
+ "llm": "0.2.9",
4
4
  "http": "0.1.1",
5
- "cli": "0.7.11"
5
+ "cli": "0.7.12"
6
6
  }
@@ -177,7 +177,7 @@ export const getProjectSuccessMessage = (folderName, installSuccess, envConfigur
177
177
  note: 'Launches Temporal, Redis, PostgreSQL, API, Worker, and UI'
178
178
  }, {
179
179
  step: 'Run example workflow',
180
- command: 'npx output workflow run poke_battle --input src/workflows/poke_battle/scenarios/pikachu_vs_charmander.json',
180
+ command: 'npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json',
181
181
  note: 'Execute in a new terminal after services are running'
182
182
  }, {
183
183
  step: 'Monitor workflows',
@@ -321,7 +321,7 @@ ${createSectionHeader('RUN A WORKFLOW', '🚀')}
321
321
 
322
322
  ${ux.colorize('white', 'In a new terminal, execute:')}
323
323
 
324
- ${formatCommand('npx output workflow run example_question --input \'{"question": "Hello!"}\'')}
324
+ ${formatCommand('npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json')}
325
325
 
326
326
  ${divider}
327
327
 
@@ -12,10 +12,10 @@
12
12
  ```
13
13
  src/
14
14
  ├── shared/ # Shared code across workflows
15
- │ ├── clients/ # API clients (e.g., pokeapi.ts)
15
+ │ ├── clients/ # API clients (e.g., jina.ts)
16
16
  │ └── utils/ # Utility functions (e.g., string.ts)
17
17
  └── workflows/ # Workflow definitions
18
- └── poke_battle/ # Example workflow
18
+ └── blog_evaluator/ # Example workflow
19
19
  ├── workflow.ts # Main workflow
20
20
  ├── steps.ts # Workflow steps
21
21
  ├── evaluators.ts # Quality evaluators
@@ -81,7 +81,7 @@ This starts:
81
81
  In a new terminal:
82
82
 
83
83
  ```bash
84
- npx output workflow run poke_battle --input src/workflows/poke_battle/scenarios/pikachu_vs_charmander.json
84
+ npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json
85
85
  ```
86
86
 
87
87
  ### 5. Stop Services
@@ -0,0 +1,30 @@
1
+ import { httpClient } from '@output.ai/http';
2
+
3
+ export interface JinaReaderResponse {
4
+ code: number;
5
+ status: number;
6
+ data: {
7
+ title: string;
8
+ description: string;
9
+ url: string;
10
+ content: string;
11
+ usage: { tokens: number };
12
+ };
13
+ }
14
+
15
+ const jinaClient = httpClient( {
16
+ prefixUrl: 'https://r.jina.ai',
17
+ timeout: 30000
18
+ } );
19
+
20
+ export async function fetchBlogContent( url: string ): Promise<JinaReaderResponse> {
21
+ const response = await jinaClient.post( '', {
22
+ json: { url },
23
+ headers: {
24
+ 'Accept': 'application/json',
25
+ 'Content-Type': 'application/json',
26
+ 'X-Return-Format': 'markdown'
27
+ }
28
+ } );
29
+ return response.json() as Promise<JinaReaderResponse>;
30
+ }
@@ -0,0 +1,15 @@
1
+ export function isValidUrl( urlString: string ): boolean {
2
+ try {
3
+ const url = new URL( urlString );
4
+ return url.protocol === 'http:' || url.protocol === 'https:';
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
9
+
10
+ export function validateUrl( urlString: string ): string {
11
+ if ( !isValidUrl( urlString ) ) {
12
+ throw new Error( `Invalid URL: ${urlString}` );
13
+ }
14
+ return urlString;
15
+ }
@@ -0,0 +1,33 @@
1
+ import { evaluator, z, EvaluationNumberResult } from '@output.ai/core';
2
+ import { generateObject } from '@output.ai/llm';
3
+ import type { BlogContent } from './types.js';
4
+
5
+ const blogContentSchema = z.object( {
6
+ title: z.string(),
7
+ url: z.string(),
8
+ content: z.string(),
9
+ tokenCount: z.number()
10
+ } );
11
+
12
+ export const evaluateSignalToNoise = evaluator( {
13
+ name: 'evaluate_signal_to_noise',
14
+ description: 'Evaluate the signal-to-noise ratio of blog content',
15
+ inputSchema: blogContentSchema,
16
+ fn: async ( input: BlogContent ) => {
17
+ const { result } = await generateObject( {
18
+ prompt: 'signal_noise@v1',
19
+ variables: {
20
+ title: input.title,
21
+ content: input.content
22
+ },
23
+ schema: z.object( {
24
+ score: z.number().min( 0 ).max( 100 ).describe( 'Signal-to-noise score 0-100' )
25
+ } )
26
+ } );
27
+
28
+ return new EvaluationNumberResult( {
29
+ value: result.score,
30
+ confidence: 0.85
31
+ } );
32
+ }
33
+ } );
@@ -0,0 +1,28 @@
1
+ ---
2
+ provider: anthropic
3
+ model: claude-haiku-4-5
4
+ temperature: 0.3
5
+ maxTokens: 256
6
+ ---
7
+
8
+ <system>
9
+ You are an expert content analyst. Evaluate blog posts for their signal-to-noise ratio.
10
+ </system>
11
+
12
+ <user>
13
+ Analyze this blog post for signal-to-noise ratio.
14
+
15
+ Title: \{{ title }}
16
+
17
+ Content:
18
+ \{{ content }}
19
+
20
+ Score 0-100 where:
21
+ - 0-20: Mostly filler/noise
22
+ - 21-40: More noise than signal
23
+ - 41-60: Balanced
24
+ - 61-80: Good signal, minimal noise
25
+ - 81-100: Exceptional, dense valuable content
26
+
27
+ Return only the score.
28
+ </user>
@@ -0,0 +1,3 @@
1
+ {
2
+ "url": "https://paulgraham.com/hwh.html"
3
+ }
@@ -0,0 +1,27 @@
1
+ import { step, z } from '@output.ai/core';
2
+ import { fetchBlogContent } from '../../shared/clients/jina.js';
3
+
4
+ const blogContentSchema = z.object( {
5
+ title: z.string(),
6
+ url: z.string(),
7
+ content: z.string(),
8
+ tokenCount: z.number()
9
+ } );
10
+
11
+ export const fetchContent = step( {
12
+ name: 'fetch_blog_content',
13
+ description: 'Fetch blog content from URL using Jina Reader API',
14
+ inputSchema: z.object( {
15
+ url: z.string().url()
16
+ } ),
17
+ outputSchema: blogContentSchema,
18
+ fn: async ( { url } ) => {
19
+ const response = await fetchBlogContent( url );
20
+ return {
21
+ title: response.data.title,
22
+ url: response.data.url,
23
+ content: response.data.content,
24
+ tokenCount: response.data.usage.tokens
25
+ };
26
+ }
27
+ } );
@@ -0,0 +1,24 @@
1
+ import { z } from '@output.ai/core';
2
+
3
+ export const blogContentSchema = z.object( {
4
+ title: z.string(),
5
+ url: z.string(),
6
+ content: z.string(),
7
+ tokenCount: z.number()
8
+ } );
9
+
10
+ export const workflowInputSchema = z.object( {
11
+ url: z.string().url().describe( 'URL of the blog post to evaluate' )
12
+ } );
13
+
14
+ export const workflowOutputSchema = z.object( {
15
+ url: z.string(),
16
+ title: z.string(),
17
+ signalToNoiseScore: z.number().min( 0 ).max( 100 ),
18
+ confidence: z.number().min( 0 ).max( 1 ),
19
+ summary: z.string()
20
+ } );
21
+
22
+ export type BlogContent = z.infer<typeof blogContentSchema>;
23
+ export type WorkflowInput = z.infer<typeof workflowInputSchema>;
24
+ export type WorkflowOutput = z.infer<typeof workflowOutputSchema>;
@@ -0,0 +1,12 @@
1
+ export function createWorkflowOutput(
2
+ blogContent: { url: string; title: string },
3
+ score: number
4
+ ) {
5
+ return {
6
+ url: blogContent.url,
7
+ title: blogContent.title,
8
+ signalToNoiseScore: score,
9
+ confidence: 0.85,
10
+ summary: `Signal-to-noise score: ${score}/100`
11
+ };
12
+ }
@@ -0,0 +1,25 @@
1
+ import { workflow, z } from '@output.ai/core';
2
+ import { validateUrl } from '../../shared/utils/url.js';
3
+ import { fetchContent } from './steps.js';
4
+ import { evaluateSignalToNoise } from './evaluators.js';
5
+ import { createWorkflowOutput } from './utils.js';
6
+ import { workflowInputSchema, workflowOutputSchema } from './types.js';
7
+
8
+ export default workflow( {
9
+ name: 'blog_evaluator',
10
+ description: '{{description}}',
11
+ inputSchema: workflowInputSchema,
12
+ outputSchema: workflowOutputSchema,
13
+ fn: async ( input ) => {
14
+ const validatedUrl = validateUrl( input.url );
15
+ const blogContent = await fetchContent( { url: validatedUrl } );
16
+ const evaluation = await evaluateSignalToNoise( blogContent );
17
+
18
+ return createWorkflowOutput( blogContent, evaluation.value );
19
+ },
20
+ options: {
21
+ retry: {
22
+ maximumAttempts: 3
23
+ }
24
+ }
25
+ } );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/cli",
3
- "version": "0.7.11-dev.pr263-8f8e94a",
3
+ "version": "0.7.12-dev.pr263-5d2eaa9",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,9 +24,8 @@
24
24
  "dependencies": {
25
25
  "@anthropic-ai/claude-agent-sdk": "0.1.71",
26
26
  "@inquirer/prompts": "7.9.0",
27
- "@oclif/core": "4.5.6",
28
- "@oclif/plugin-help": "6.2.33",
29
- "@oclif/plugin-plugins": "5.4.50",
27
+ "@oclif/core": "4.8.0",
28
+ "@oclif/plugin-help": "6.2.37",
30
29
  "@output.ai/http": ">=0.0.1",
31
30
  "@output.ai/llm": ">=0.0.1",
32
31
  "change-case": "5.4.4",
@@ -46,7 +45,7 @@
46
45
  "@types/debug": "4.1.12",
47
46
  "@types/handlebars": "4.1.0",
48
47
  "copyfiles": "2.4.1",
49
- "orval": "7.13.2"
48
+ "orval": "7.20.0"
50
49
  },
51
50
  "license": "Apache-2.0",
52
51
  "publishConfig": {
@@ -60,8 +59,7 @@
60
59
  "dirname": "output",
61
60
  "commands": "./dist/commands",
62
61
  "plugins": [
63
- "@oclif/plugin-help",
64
- "@oclif/plugin-plugins"
62
+ "@oclif/plugin-help"
65
63
  ],
66
64
  "topicSeparator": " ",
67
65
  "topics": {
@@ -1,43 +0,0 @@
1
- import { httpClient, HttpClientOptions } from '@output.ai/http';
2
-
3
- export interface Pokemon {
4
- name: string;
5
- types: Array<{ type: { name: string } }>;
6
- abilities: Array<{ ability: { name: string }; is_hidden: boolean }>;
7
- stats: Array<{ stat: { name: string }; base_stat: number }>;
8
- }
9
-
10
- const basePokeApiClient = httpClient( {
11
- prefixUrl: 'https://pokeapi.co/api/v2'
12
- } );
13
-
14
- const pokemonClient = basePokeApiClient.extend( options => ( {
15
- prefixUrl: `${options.prefixUrl}/pokemon`,
16
- timeout: 10000
17
- } ) as HttpClientOptions );
18
-
19
- /**
20
- * Fetch Pokemon data from PokeAPI, extracting only the relevant fields.
21
- * The full API response is huge (sprites, all moves, etc.), so we filter
22
- * to just what's needed for the battle screenplay.
23
- */
24
- export async function getPokemon( name: string ): Promise<Pokemon> {
25
- const response = await pokemonClient.get( name );
26
- const data = await response.json() as Record<string, unknown>;
27
-
28
- // Extract only the fields we need to avoid sending huge payloads to LLM
29
- return {
30
- name: data.name as string,
31
- types: ( data.types as Array<{ type: { name: string } }> ).map( t => ( {
32
- type: { name: t.type.name }
33
- } ) ),
34
- abilities: ( data.abilities as Array<{ ability: { name: string }; is_hidden: boolean }> ).map( a => ( {
35
- ability: { name: a.ability.name },
36
- is_hidden: a.is_hidden
37
- } ) ),
38
- stats: ( data.stats as Array<{ stat: { name: string }; base_stat: number }> ).map( s => ( {
39
- stat: { name: s.stat.name },
40
- base_stat: s.base_stat
41
- } ) )
42
- };
43
- }
@@ -1,47 +0,0 @@
1
- import { evaluator, z, EvaluationNumberResult } from '@output.ai/core';
2
- import { generateObject } from '@output.ai/llm';
3
- import type { Pokemon } from '../../shared/clients/pokeapi.js';
4
-
5
- const pokemonSchema = z.object( {
6
- name: z.string(),
7
- types: z.array( z.object( {
8
- type: z.object( { name: z.string() } )
9
- } ) ),
10
- abilities: z.array( z.object( {
11
- ability: z.object( { name: z.string() } ),
12
- is_hidden: z.boolean()
13
- } ) ),
14
- stats: z.array( z.object( {
15
- stat: z.object( { name: z.string() } ),
16
- base_stat: z.number()
17
- } ) )
18
- } );
19
-
20
- export const evaluateBattleRealism = evaluator( {
21
- name: 'evaluate_battle_realism',
22
- description: 'Evaluate how realistic the Pokemon battle screenplay is',
23
- inputSchema: z.object( {
24
- screenplay: z.string(),
25
- pokemon1: pokemonSchema,
26
- pokemon2: pokemonSchema
27
- } ),
28
- fn: async ( input: { screenplay: string; pokemon1: Pokemon; pokemon2: Pokemon } ) => {
29
- const { result } = await generateObject( {
30
- prompt: 'evaluate_realism@v1',
31
- variables: {
32
- pokemon1Name: input.pokemon1.name,
33
- pokemon1Description: JSON.stringify( input.pokemon1 ),
34
- pokemon2Name: input.pokemon2.name,
35
- pokemon2Description: JSON.stringify( input.pokemon2 ),
36
- screenplay: input.screenplay
37
- },
38
- schema: z.object( {
39
- score: z.number().min( 0 ).max( 100 ).describe( 'Realism score 0-100' )
40
- } )
41
- } );
42
- return new EvaluationNumberResult( {
43
- value: result.score,
44
- confidence: 0.9
45
- } );
46
- }
47
- } );
@@ -1,26 +0,0 @@
1
- ---
2
- provider: anthropic
3
- model: claude-haiku-4-5
4
- temperature: 0.7
5
- maxTokens: 8192
6
- ---
7
-
8
- <user>
9
- Evaluate this Pokemon battle screenplay for realism.
10
-
11
- Consider:
12
- - Are the moves/abilities used consistent with each Pokemon's actual abilities?
13
- - Is the battle outcome realistic given their types and stats?
14
- - Does the pacing feel like a real battle?
15
-
16
- \{{ pokemon1Name }}'s Stat Sheet:
17
-
18
- \{{ pokemon1Description }}
19
-
20
- \{{ pokemon2Name }}'s Stat Sheet:
21
-
22
- \{{ pokemon2Description }}
23
-
24
- Screenplay:
25
- \{{ screenplay }}
26
- </user>
@@ -1,20 +0,0 @@
1
- ---
2
- provider: anthropic
3
- model: claude-haiku-4-5
4
- temperature: 0.7
5
- maxTokens: 8192
6
- ---
7
-
8
- <user>
9
- Write a dramatic 5-minute screenplay of a Pokemon battle between \{{ pokemon1Name }} and \{{ pokemon2Name }}.
10
-
11
- \{{ pokemon1Name }}'s Stat Sheet:
12
-
13
- \{{ pokemon1Description }}
14
-
15
- \{{ pokemon2Name }}'s Stat Sheet:
16
-
17
- \{{ pokemon2Description }}
18
-
19
- Include dramatic moments, skill usage that matches their actual abilities, and a decisive outcome based on their type matchups. Format it as a proper screenplay with scene descriptions and dialogue.
20
- </user>
@@ -1,4 +0,0 @@
1
- {
2
- "pokemon1Name": "pikachu",
3
- "pokemon2Name": "charmander"
4
- }
@@ -1,58 +0,0 @@
1
- import { step, z } from '@output.ai/core';
2
- import { generateText } from '@output.ai/llm';
3
- import { getPokemon, type Pokemon } from '../../shared/clients/pokeapi.js';
4
-
5
- const pokemonSchema = z.object( {
6
- name: z.string(),
7
- types: z.array( z.object( {
8
- type: z.object( { name: z.string() } )
9
- } ) ),
10
- abilities: z.array( z.object( {
11
- ability: z.object( { name: z.string() } ),
12
- is_hidden: z.boolean()
13
- } ) ),
14
- stats: z.array( z.object( {
15
- stat: z.object( { name: z.string() } ),
16
- base_stat: z.number()
17
- } ) )
18
- } );
19
-
20
- /**
21
- * Step to fetch Pokemon data and generate a battle screenplay.
22
- * HTTP clients must be used inside steps (activities), not directly in workflows,
23
- * because workflows run in a sandboxed environment without Node.js APIs.
24
- */
25
- export const generateScreenplay = step( {
26
- name: 'generate_screenplay',
27
- description: 'Fetch Pokemon data and generate a dramatic 5-minute battle screenplay',
28
- inputSchema: z.object( {
29
- pokemon1Name: z.string().describe( 'Name of the first Pokemon' ),
30
- pokemon2Name: z.string().describe( 'Name of the second Pokemon' )
31
- } ),
32
- outputSchema: z.object( {
33
- screenplay: z.string(),
34
- pokemon1: pokemonSchema,
35
- pokemon2: pokemonSchema
36
- } ),
37
- fn: async ( input: { pokemon1Name: string; pokemon2Name: string } ) => {
38
- // Fetch Pokemon data inside the step (activities can use HTTP clients)
39
- const pokemon1 = await getPokemon( input.pokemon1Name );
40
- const pokemon2 = await getPokemon( input.pokemon2Name );
41
-
42
- const { result } = await generateText( {
43
- prompt: 'generate_screenplay@v1',
44
- variables: {
45
- pokemon1Name: pokemon1.name,
46
- pokemon2Name: pokemon2.name,
47
- pokemon1Description: JSON.stringify( pokemon1 ),
48
- pokemon2Description: JSON.stringify( pokemon2 )
49
- }
50
- } );
51
-
52
- return {
53
- screenplay: result,
54
- pokemon1: pokemon1 as Pokemon,
55
- pokemon2: pokemon2 as Pokemon
56
- };
57
- }
58
- } );
@@ -1,7 +0,0 @@
1
- export function createWorkflowOutput( screenplay: string, confidenceScore: number ) {
2
- return {
3
- screenplay,
4
- confidenceScore,
5
- summary: `Battle screenplay (${confidenceScore}% confidence)`
6
- };
7
- }
@@ -1,45 +0,0 @@
1
- import { workflow, z } from '@output.ai/core';
2
- import { lowercase } from '../../shared/utils/string.js';
3
- import { generateScreenplay } from './steps.js';
4
- import { evaluateBattleRealism } from './evaluators.js';
5
- import { createWorkflowOutput } from './utils.js';
6
-
7
- export default workflow( {
8
- name: 'poke_battle',
9
- description: '{{description}}',
10
- inputSchema: z.object( {
11
- pokemon1Name: z.string().describe( 'Name of the first Pokemon' ),
12
- pokemon2Name: z.string().describe( 'Name of the second Pokemon' )
13
- } ),
14
- outputSchema: z.object( {
15
- screenplay: z.string().describe( 'A 5-minute battle screenplay' ),
16
- confidenceScore: z.number().describe( 'Realism confidence score 0-100' ),
17
- summary: z.string().describe( 'Battle summary with confidence' )
18
- } ),
19
- fn: async input => {
20
- // Use shared util to normalize names
21
- const name1 = lowercase( input.pokemon1Name );
22
- const name2 = lowercase( input.pokemon2Name );
23
-
24
- // Generate the battle screenplay (fetches Pokemon data inside the step)
25
- const { screenplay, pokemon1, pokemon2 } = await generateScreenplay( {
26
- pokemon1Name: name1,
27
- pokemon2Name: name2
28
- } );
29
-
30
- // Evaluate realism
31
- const evaluation = await evaluateBattleRealism( {
32
- screenplay,
33
- pokemon1,
34
- pokemon2
35
- } );
36
-
37
- // Use local util to format output
38
- return createWorkflowOutput( screenplay, evaluation.value );
39
- },
40
- options: {
41
- retry: {
42
- maximumAttempts: 3
43
- }
44
- }
45
- } );