numind/NuExtract3
36556k
1{%- if not messages %}2 {{- raise_exception('No messages provided.') }}3{%- endif %}4{%- set image_count = namespace(value=0) %}5{%- set image_placeholder = '<|vision_start|><|image_pad|><|vision_end|>' -%}6{%- set mode = mode | default('content') -%}7{%- if template -%}{%- set mode = 'structured' -%}{%- endif -%}8{%- if not template and mode == 'structured' %}9 {{- raise_exception('`structured` mode specified but no `template` provided.') }}10{%- endif %}11{%- if mode not in ['structured', 'content', 'template-generation', 'document-detection', 'markdown'] -%}{%- set mode = 'content' -%}{%- endif -%}12{%- if mode == 'markdown' %}{%- set mode = 'content' -%}{%- endif %}13{%- set enable_thinking = enable_thinking | default(False) -%}14{%- if mode not in ['structured', 'content'] and enable_thinking %}15 {{- raise_exception('`enable_thinking` can only be `True` for `structured` and `content` modes.') }}16{%- endif %}17{%- set has_examples = namespace(flag=false) -%}18{%- if mode != 'structured' -%}{%- set has_examples = false -%}{%- endif -%}19{# MACRO TO RENDER MESSAGE CONTENT #}20{%- macro render_content(content, do_vision_count, is_system_content=false) %}21 {%- if content is string %}22 {{- content }}23 {%- elif content is iterable and content is not mapping %}24 {%- for item in content %}25 {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}26 {%- if is_system_content %}27 {{- raise_exception('System message cannot contain images.') }}28 {%- endif %}29 {%- if do_vision_count %}30 {%- set image_count.value = image_count.value + 1 %}31 {%- endif %}32 {%- if add_vision_id %}33 {{- 'Picture ' ~ image_count.value ~ ': ' }}34 {%- endif %}35 {{- '<|vision_start|><|image_pad|><|vision_end|>\n' }}36 {%- elif 'text' in item %}37 {{- item.text + '\n' }}38 {%- else %}39 {{- raise_exception('Unexpected item type in content.') }}40 {%- endif %}41 {%- endfor %}42 {%- elif content is none or content is undefined %}43 {{- '' }}44 {%- else %}45 {{- raise_exception('Unexpected content type.') }}46 {%- endif %}47{%- endmacro %}48{# SYSTEM MESSAGE #}49{%- if messages[0].role == 'system' %}50 {%- set content = render_content(messages[0].content, false, true)|trim %}51 {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}52{%- endif %}53{# USER MESSAGE #}54{{- '<|im_start|>user\n' -}}55{{- '【task】' + mode|replace("-", " ") + '\n' -}}56{# Template Section (for structured task): specifies template, instructions, examples, previous_output #}57{%- if mode == 'structured' -%}58 {{- '【template_start】' + template + '【template_end】\n' -}}59 {# Instructions Section #}60 {%- if instructions -%}61 {{- '【instructions_start】' + instructions + '【instructions_end】\n'-}}62 {%- endif -%}63 {# Examples Section (only for extraction tasks) #}64 {%- for message in messages -%}65 {%- if message.role == 'developer' and 'content' in message -%}66 {# Validate that there is at least one input and one output contents #}67 {%- set example_inputs = message.content[:-1] -%}68 {%- set example_output_part = message.content[-1] -%}69 {%- if example_inputs|length > 0 -%}70 {%- if not has_examples.flag -%}71 {{- '【examples_start】\n' -}}72 {%- set has_examples.flag = true -%}73 {%- endif -%}74 {{- '【example_input_start】' + render_content(example_inputs, true)|trim + '【example_input_end】\n' -}}75 {# Example output: only keep the text of the first output content #}76 {%- set output_text = '' -%}77 {%- if example_output_part is string -%}78 {%- set output_text = example_output_part -%}79 {%- elif example_output_part.text is defined -%}80 {%- set output_text = example_output_part.text -%}81 {%- endif -%}82 {{- '【example_output_start】' + output_text|trim + '【example_output_end】\n' -}}83 {%- if loop.last and has_examples.flag -%}84 {{- '【examples_end】\n' -}}85 {%- endif -%}86 {%- endif -%}87 {%- endif -%}88 {%- endfor -%}89 {# Previous Output Section #}90 {%- if previous_output -%}91 {{- '【previous_output_start】' + previous_output + '【previous_output_end】\n' -}}92 {%- endif -%}93{%- endif -%}94{{- '【document_start】\n' -}}95{# PROCESS PROVIDED USER MESSAGES (RENDERED INTO A SINGLE ONE) #}96{%- for message in messages -%}97 {%- if message.role == "system" %}98 {%- if not loop.first %}99 {{- raise_exception('System message must be at the beginning.') }}100 {%- endif %}101 {%- elif message.role == 'user' and message.name != "example" -%}102 {%- set content = render_content(message.content, true)|trim %}103 {{- content + '\n' -}}104 {# {%- elif message.role == 'assistant' and not loop.last %}105 llama.cpp renders a synthetic init example with an assistant turn in106 the middle; ignore it so valid NuExtract prompts render unchanged.107 {{- raise_exception('Assistant message must be at the end.') }} #}108 {%- endif %}109{%- endfor -%}110{{- '【document_end】<|im_end|>\n' -}}111{# ASSISTANT MESSAGE #}112{%- if messages[-1].role == 'assistant' %}113 {%- if add_generation_prompt -%}114 {{- raise_exception('`add_generation_prompt` can only be `True` when no assistant message is provided.') }}115 {%- endif %}116 {%- set content = render_content(messages[-1].content, true)|trim %}117 {%- set reasoning_content = '' %}118 {%- if messages[-1].reasoning_content is string %}119 {%- set reasoning_content = messages[-1].reasoning_content %}120 {%- else %}121 {%- if '</think>' in content %}122 {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}123 {%- set content = content.split('</think>')[-1].lstrip('\n') %}124 {%- endif %}125 {%- endif %}126 {%- set reasoning_content = reasoning_content|trim %}127 {% generation %}128 {{- '<|im_start|>assistant\n<think>\n' + reasoning_content + '\n</think>\n\n' + content + '<|im_end|>\n' -}}129 {% endgeneration %}130{%- endif -%}131{# GENERATION PROMPT #}132{%- if add_generation_prompt -%}133 {{- '<|im_start|>assistant\n' -}}134 {%- if not enable_thinking -%}135 {{- '<think>\n\n</think>\n\n' -}}136 {%- else %}137 {{- '<think>\n' -}}138 {%- endif %}139{%- endif -%}140 