1717)
1818from comfy_extras .nodes_images import SVG
1919
20- _ARROW_MODELS = ["arrow-1.1" , "arrow-1.1-max" , "arrow-preview" ]
20+ _ARROW_MODELS = ["arrow-2" , "arrow-2-telos" , "arrow-1.1" , "arrow-1.1-max" , "arrow-preview" ]
21+ _EFFORT_LEVELS = ["low" , "medium" , "high" , "xhigh" ]
22+ _TOKEN_MODEL_RATES = {"arrow-2" : (4 , 20 ), "arrow-2-telos" : (6 , 30 )}
23+ _FIXED_GENERATION_USD = {"arrow-1.1" : 0.286 , "arrow-1.1-max" : 0.3575 , "arrow-preview" : 0.429 }
24+ _FIXED_VECTORIZATION_USD = {"arrow-1.1" : 0.2145 , "arrow-1.1-max" : 0.286 , "arrow-preview" : 0.429 }
25+
26+ _GENERATION_TOKENS = {
27+ "arrow-2" : {
28+ "low" : (713 , 1100 , 723 , 20000 ),
29+ "medium" : (713 , 2200 , 723 , 34000 ),
30+ "high" : (713 , 2200 , 723 , 36000 ),
31+ "xhigh" : (713 , 2200 , 723 , 38000 ),
32+ },
33+ "arrow-2-telos" : {
34+ "low" : (476 , 1100 , 749 , 36000 ),
35+ "medium" : (476 , 1300 , 749 , 67000 ),
36+ "high" : (476 , 1300 , 749 , 71000 ),
37+ "xhigh" : (476 , 10000 , 749 , 106000 ),
38+ },
39+ }
40+ _VECTORIZATION_TOKENS = {
41+ "arrow-2" : {
42+ "low" : (591 , 600 , 783 , 23000 ),
43+ "medium" : (591 , 1300 , 783 , 36000 ),
44+ "high" : (591 , 1300 , 783 , 38000 ),
45+ "xhigh" : (591 , 1300 , 783 , 40000 ),
46+ },
47+ "arrow-2-telos" : {
48+ "low" : (394 , 300 , 522 , 47000 ),
49+ "medium" : (394 , 300 , 522 , 48000 ),
50+ "high" : (394 , 300 , 522 , 101000 ),
51+ "xhigh" : (394 , 12000 , 522 , 115000 ),
52+ },
53+ }
54+
55+
56+ def _effort_bands (model , tokens ):
57+ rate_in , rate_out = _TOKEN_MODEL_RATES [model ]
58+ effort = "widgets.reasoning_effort"
59+
60+ def band (level ):
61+ min_in , min_out , max_in , max_out = tokens [model ][level ]
62+ return (
63+ '{"type":"range_usd",'
64+ f'"min_usd":({ min_in } * { rate_in } + { min_out } * { rate_out } ) * 1.43 / 1000000,'
65+ f'"max_usd":({ max_in } * { rate_in } + { max_out } * { rate_out } ) * 1.43 / 1000000,'
66+ '"format":{"approximate":true}}'
67+ )
68+
69+ levels = [f'{ effort } = "{ level } " ? { band (level )} ' for level in ("low" , "medium" , "xhigh" )]
70+ return " : " .join ([* levels , band ("high" )])
71+
72+
73+ def _arrow_price_badge (tokens , fixed_usd ):
74+ branches = [f'widgets.model = "{ model } " ? {{"type":"usd","usd":{ usd } }}' for model , usd in fixed_usd .items ()]
75+ branches .append (f'widgets.model = "arrow-2-telos" ? ({ _effort_bands ("arrow-2-telos" , tokens )} )' )
76+ branches .append (f'({ _effort_bands ("arrow-2" , tokens )} )' )
77+ return IO .PriceBadge (
78+ depends_on = IO .PriceBadgeDepends (widgets = ["model" , "reasoning_effort" ]),
79+ expr = "(" + " : " .join (branches ) + ")" ,
80+ )
81+
82+
83+ def _reasoning_effort_input ():
84+ return IO .Combo .Input (
85+ "reasoning_effort" ,
86+ options = _EFFORT_LEVELS ,
87+ default = "high" ,
88+ optional = True ,
89+ tooltip = "How much reasoning the model spends before drawing. Higher levels improve "
90+ "detail and cost more tokens. Only used by the Arrow 2 models." ,
91+ )
2192
2293
2394def _arrow_sampling_inputs ():
@@ -104,6 +175,7 @@ def define_schema(cls):
104175 tooltip = "Seed to determine if node should re-run; "
105176 "actual results are nondeterministic regardless of seed." ,
106177 ),
178+ _reasoning_effort_input (),
107179 ],
108180 outputs = [
109181 IO .SVG .Output (),
@@ -114,18 +186,7 @@ def define_schema(cls):
114186 IO .Hidden .unique_id ,
115187 ],
116188 is_api_node = True ,
117- price_badge = IO .PriceBadge (
118- depends_on = IO .PriceBadgeDepends (widgets = ["model" ]),
119- expr = """
120- (
121- $contains(widgets.model, "max")
122- ? {"type":"usd","usd":0.3575}
123- : $contains(widgets.model, "preview")
124- ? {"type":"usd","usd":0.429}
125- : {"type":"usd","usd":0.286}
126- )
127- """ ,
128- ),
189+ price_badge = _arrow_price_badge (_GENERATION_TOKENS , _FIXED_GENERATION_USD ),
129190 )
130191
131192 @classmethod
@@ -134,6 +195,7 @@ async def execute(
134195 prompt : str ,
135196 model : dict ,
136197 seed : int ,
198+ reasoning_effort : str = None ,
137199 instructions : str = None ,
138200 reference_images : IO .Autogrow .Type = None ,
139201 ) -> IO .NodeOutput :
@@ -145,8 +207,6 @@ async def execute(
145207 for key in reference_images :
146208 url = await upload_image_to_comfyapi (cls , reference_images [key ], mime_type = "image/png" )
147209 references .append (QuiverImageObject (url = url ))
148- if len (references ) > 4 :
149- raise ValueError ("Maximum 4 reference images are allowed." )
150210
151211 instructions_val = instructions .strip () if instructions else None
152212 if instructions_val == "" :
@@ -158,6 +218,7 @@ async def execute(
158218 response_model = QuiverSVGResponse ,
159219 data = QuiverTextToSVGRequest (
160220 model = model ["model" ],
221+ reasoning_effort = reasoning_effort if model ["model" ] in _TOKEN_MODEL_RATES else None ,
161222 prompt = prompt ,
162223 instructions = instructions_val ,
163224 references = references ,
@@ -198,10 +259,11 @@ def define_schema(cls):
198259 [
199260 IO .Int .Input (
200261 "target_size" ,
201- default = 1024 ,
202- min = 128 ,
262+ default = 0 ,
263+ min = 0 ,
203264 max = 4096 ,
204- tooltip = "Square resize target in pixels." ,
265+ tooltip = "Square resize target in pixels, 128 to 4096. 0 keeps the source "
266+ "image size, which vectorizes more cleanly than forcing a resize." ,
205267 advanced = True ,
206268 ),
207269 * _arrow_sampling_inputs (),
@@ -220,6 +282,7 @@ def define_schema(cls):
220282 tooltip = "Seed to determine if node should re-run; "
221283 "actual results are nondeterministic regardless of seed." ,
222284 ),
285+ _reasoning_effort_input (),
223286 ],
224287 outputs = [
225288 IO .SVG .Output (),
@@ -230,18 +293,7 @@ def define_schema(cls):
230293 IO .Hidden .unique_id ,
231294 ],
232295 is_api_node = True ,
233- price_badge = IO .PriceBadge (
234- depends_on = IO .PriceBadgeDepends (widgets = ["model" ]),
235- expr = """
236- (
237- $contains(widgets.model, "max")
238- ? {"type":"usd","usd":0.3575}
239- : $contains(widgets.model, "preview")
240- ? {"type":"usd","usd":0.429}
241- : {"type":"usd","usd":0.286}
242- )
243- """ ,
244- ),
296+ price_badge = _arrow_price_badge (_VECTORIZATION_TOKENS , _FIXED_VECTORIZATION_USD ),
245297 )
246298
247299 @classmethod
@@ -251,6 +303,7 @@ async def execute(
251303 auto_crop : bool ,
252304 model : dict ,
253305 seed : int ,
306+ reasoning_effort : str = None ,
254307 ) -> IO .NodeOutput :
255308 image_url = await upload_image_to_comfyapi (cls , image , mime_type = "image/png" )
256309
@@ -260,9 +313,10 @@ async def execute(
260313 response_model = QuiverSVGResponse ,
261314 data = QuiverImageToSVGRequest (
262315 model = model ["model" ],
316+ reasoning_effort = reasoning_effort if model ["model" ] in _TOKEN_MODEL_RATES else None ,
263317 image = QuiverImageObject (url = image_url ),
264318 auto_crop = auto_crop if auto_crop else None ,
265- target_size = model .get ("target_size" ),
319+ target_size = model .get ("target_size" ) or None ,
266320 temperature = model .get ("temperature" ),
267321 top_p = model .get ("top_p" ),
268322 presence_penalty = model .get ("presence_penalty" ),
0 commit comments