katuni4ka commited on
Commit
a93cb08
·
verified ·
1 Parent(s): 18a9411

Upload 23 files

Browse files
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
config.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "DeepseekV32ForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "auto_map": {
8
+ "AutoConfig": "configuration_deepseek_v32.DeepseekV32Config",
9
+ "AutoModelForCausalLM": "modeling_deepseek_v32.DeepseekV32ForCausalLM"
10
+ },
11
+ "dtype": "bfloat16",
12
+ "ep_size": 1,
13
+ "firts_k_dense_replace": 1,
14
+ "head_dim": 2,
15
+ "hidden_act": "silu",
16
+ "hidden_size": 32,
17
+ "index_head_dim": 4,
18
+ "index_n_heads": 4,
19
+ "index_topk": 32,
20
+ "initializer_range": 0.02,
21
+ "intermediate_size": 16,
22
+ "kv_lora_rank": 2,
23
+ "tie_word_embeddings": true,
24
+ "max_position_embeddings": 163840,
25
+ "mlp_layer_types": [
26
+ "dense",
27
+ "sparse"
28
+ ],
29
+ "model_type": "deepseek_v32",
30
+ "moe_intermediate_size": 4,
31
+ "moe_layer_freq": 1,
32
+ "n_group": 1,
33
+ "n_routed_experts": 16,
34
+ "n_shared_experts": 1,
35
+ "norm_topk_prob": true,
36
+ "num_attention_heads": 2,
37
+ "num_experts_per_tok": 8,
38
+ "num_hidden_layers": 2,
39
+ "num_key_value_heads": 2,
40
+ "num_nextn_predict_layers": 1,
41
+ "q_lora_rank": 2,
42
+ "qk_head_dim": 6,
43
+ "qk_nope_head_dim": 4,
44
+ "qk_rope_head_dim": 2,
45
+ "rms_norm_eps": 1e-06,
46
+ "rope_interleave": true,
47
+ "rope_scaling": {
48
+ "beta_fast": 32.0,
49
+ "beta_slow": 1.0,
50
+ "factor": 40.0,
51
+ "mscale": 1.0,
52
+ "mscale_all_dim": 1.0,
53
+ "original_max_position_embeddings": 4096,
54
+ "rope_theta": 10000,
55
+ "rope_type": "yarn",
56
+ "type": "yarn"
57
+ },
58
+ "rope_theta": 10000.0,
59
+ "routed_scaling_factor": 2.5,
60
+ "scoring_func": "sigmoid",
61
+ "topk_group": 4,
62
+ "topk_method": "noaux_tc",
63
+ "transformers_version": "4.57.1",
64
+ "use_cache": true,
65
+ "v_head_dim": 2,
66
+ "vocab_size": 129280
67
+ }
configuration_deepseek_v32.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 the HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ from transformers.configuration_utils import PretrainedConfig
17
+
18
+
19
+ class DeepseekV32Config(PretrainedConfig):
20
+ r"""
21
+ This is the configuration class to store the configuration of a [`DeepseekV32Model`].
22
+ Configuration objects inherit from [`PreTrainedConfig`] and can be used to control the model outputs. Read the
23
+ documentation from [`PretrainedConfig`] for more information.
24
+
25
+
26
+ Args:
27
+ vocab_size (`int`, *optional*, defaults to 154880):
28
+ Vocabulary size of the Deep model. Defines the number of different tokens that can be represented by the
29
+ `inputs_ids` passed when calling [`DeepseekV32Model`]
30
+ hidden_size (`int`, *optional*, defaults to 6144):
31
+ Dimension of the hidden representations.
32
+ intermediate_size (`int`, *optional*, defaults to 12288):
33
+ Dimension of the MLP representations.
34
+ moe_intermediate_size (`int`, *optional*, defaults to 2048):
35
+ Dimension of the MoE representations.
36
+ num_hidden_layers (`int`, *optional*, defaults to 78):
37
+ Number of hidden layers in the Transformer decoder.
38
+ num_attention_heads (`int`, *optional*, defaults to 64):
39
+ Number of attention heads for each attention layer in the Transformer decoder.
40
+ num_key_value_heads (`int`, *optional*, defaults to 64):
41
+ This is the number of key_value heads that should be used to implement Grouped Query Attention. If
42
+ `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
43
+ `num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When
44
+ converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
45
+ by meanpooling all the original heads within that group. For more details, check out [this
46
+ paper](https://huggingface.co/papers/2305.13245). If it is not specified, will default to
47
+ `num_attention_heads`.
48
+ n_shared_experts (`int`, *optional*, defaults to 1):
49
+ Number of shared experts.
50
+ n_routed_experts (`int`, *optional*, defaults to 256):
51
+ Number of routed experts.
52
+ routed_scaling_factor (`float`, *optional*, defaults to 2.5):
53
+ Scaling factor or routed experts.
54
+ kv_lora_rank (`int`, *optional*, defaults to 512):
55
+ Rank of the LoRA matrices for key and value projections.
56
+ q_lora_rank (`int`, *optional*, defaults to 2048):
57
+ Rank of the LoRA matrices for query projections.
58
+ qk_rope_head_dim (`int`, *optional*, defaults to 64):
59
+ Dimension of the query/key heads that use rotary position embeddings.
60
+ v_head_dim (`int`, *optional*, defaults to 256):
61
+ Dimension of the value heads.
62
+ qk_nope_head_dim (`int`, *optional*, defaults to 192):
63
+ Dimension of the query/key heads that don't use rotary position embeddings.
64
+ n_group (`int`, *optional*, defaults to 1):
65
+ Number of groups for routed experts.
66
+ topk_group (`int`, *optional*, defaults to 1):
67
+ Number of selected groups for each token(for each token, ensuring the selected experts is only within `topk_group` groups).
68
+ num_experts_per_tok (`int`, *optional*, defaults to 8):
69
+ Number of selected experts, None means dense model.
70
+ norm_topk_prob (`bool`, *optional*, defaults to `True`):
71
+ Whether to normalize the weights of the routed experts.
72
+ hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
73
+ The non-linear activation function (function or string) in the decoder.
74
+ max_position_embeddings (`int`, *optional*, defaults to 202752):
75
+ The maximum sequence length that this model might ever be used with.
76
+ initializer_range (`float`, *optional*, defaults to 0.02):
77
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
78
+ rms_norm_eps (`float`, *optional*, defaults to 1e-05):
79
+ The epsilon used by the rms normalization layers.
80
+ use_cache (`bool`, *optional*, defaults to `True`):
81
+ Whether or not the model should return the last key/values attentions (not used by all models). Only
82
+ relevant if `config.is_decoder=True`.
83
+ pad_token_id (`int`, *optional*):
84
+ Padding token id.
85
+ bos_token_id (`int`, *optional*, defaults to 0):
86
+ Beginning of stream token id.
87
+ eos_token_id (`int`, *optional*, defaults to 1):
88
+ End of stream token id.
89
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
90
+ Whether to tie weight embeddings
91
+ rope_scaling (`Dict`, *optional*):
92
+ Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling
93
+ strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is
94
+ `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update
95
+ `max_position_embeddings` to the expected new maximum.
96
+ rope_theta (`float`, *optional*, defaults to 10000.0):
97
+ The base period of the RoPE embeddings.
98
+ rope_interleave (`bool`, *optional*, defaults to `True`):
99
+ Whether to interleave the rotary position embeddings.
100
+ first_k_dense_replace (`int`, *optional*, defaults to 3):
101
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head). \--k dense layers--/
102
+ attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
103
+ Whether to use a bias in the query, key, value and output projection layers during self-attention.
104
+ attention_dropout (`float`, *optional*, defaults to 0.0):
105
+ The dropout ratio for the attention probabilities.
106
+ index_topk (`int`, *optional*, defaults to 2048):
107
+ Number of top tokens selected by the indexer for retrieval/attention in each step.
108
+ """
109
+
110
+ model_type = "deepseek_v32"
111
+ keys_to_ignore_at_inference = ["past_key_values"]
112
+ base_model_tp_plan = { # TODO: only replicate attention layers when > first_k_dense_replace
113
+ "layers.*.mlp.experts.*.gate_proj": "local_colwise",
114
+ "layers.*.mlp.experts.*.up_proj": "local_colwise",
115
+ "layers.*.mlp.experts.*.down_proj": "local_rowwise",
116
+ "layers.*.mlp.experts.*": "local", # each expert is wrapped in a module list
117
+ "layers.*.mlp.shared_experts.gate_proj": "local_colwise",
118
+ "layers.*.mlp.shared_experts.up_proj": "local_colwise",
119
+ "layers.*.mlp.shared_experts.down_proj": "local_rowwise",
120
+ "layers.*.mlp.shared_experts": "local",
121
+ "layers.*.mlp.gate_proj": "local_colwise",
122
+ "layers.*.mlp.up_proj": "local_colwise",
123
+ "layers.*.mlp.down_proj": "local_rowwise",
124
+ "layers.*.mlp": "gather", # This is the only moment where results are gathered
125
+ }
126
+ base_model_pp_plan = {
127
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
128
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
129
+ "norm": (["hidden_states"], ["hidden_states"]),
130
+ }
131
+
132
+ def __init__(
133
+ self,
134
+ vocab_size: int | None = 154880,
135
+ hidden_size: int | None = 6144,
136
+ intermediate_size: int | None = 12288,
137
+ moe_intermediate_size: int | None = 2048,
138
+ num_hidden_layers: int | None = 78,
139
+ num_attention_heads: int | None = 64,
140
+ num_key_value_heads: int | None = 64,
141
+ n_shared_experts: int | None = 1,
142
+ n_routed_experts: int | None = 256,
143
+ routed_scaling_factor: float | None = 2.5,
144
+ kv_lora_rank: int | None = 512,
145
+ q_lora_rank: int | None = 2048,
146
+ qk_rope_head_dim: int | None = 64,
147
+ v_head_dim: int | None = 256,
148
+ qk_nope_head_dim: int | None = 192,
149
+ n_group: int | None = 1,
150
+ topk_group: int | None = 1,
151
+ num_experts_per_tok: int | None = 8,
152
+ norm_topk_prob: bool | None = True,
153
+ hidden_act: str | None = "silu",
154
+ max_position_embeddings: int | None = 202752,
155
+ initializer_range: float | None = 0.02,
156
+ rms_norm_eps: int | None = 1e-5,
157
+ use_cache: bool | None = True,
158
+ pad_token_id: int | None = None,
159
+ bos_token_id: int | None = 0,
160
+ eos_token_id: int | None = 1,
161
+ tie_word_embeddings: bool | None = False,
162
+ rope_scaling: dict = None,
163
+ rope_theta: float =10000.0,
164
+ rope_interleave: bool | None = True,
165
+ first_k_dense_replace: int = 3,
166
+ attention_bias: bool | None = False,
167
+ attention_dropout: float | None = 0.0,
168
+ index_topk: int | None = 2048,
169
+ **kwargs,
170
+ ):
171
+ self.vocab_size = vocab_size
172
+ self.max_position_embeddings = max_position_embeddings
173
+ self.hidden_size = hidden_size
174
+ self.intermediate_size = intermediate_size
175
+ self.num_hidden_layers = num_hidden_layers
176
+ self.firts_k_dense_replace = first_k_dense_replace
177
+
178
+ self.moe_intermediate_size = moe_intermediate_size
179
+ self.num_attention_heads = num_attention_heads
180
+ self.n_shared_experts = n_shared_experts
181
+ self.n_routed_experts = n_routed_experts
182
+ self.routed_scaling_factor = routed_scaling_factor
183
+ self.kv_lora_rank = kv_lora_rank
184
+ self.q_lora_rank = q_lora_rank
185
+ self.qk_rope_head_dim = qk_rope_head_dim
186
+ self.v_head_dim = v_head_dim
187
+ self.qk_nope_head_dim = qk_nope_head_dim
188
+ self.qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
189
+ self.head_dim = qk_rope_head_dim
190
+ self.n_group = n_group
191
+ self.topk_group = topk_group
192
+ self.num_experts_per_tok = num_experts_per_tok
193
+ self.norm_topk_prob = norm_topk_prob
194
+ self.rope_interleave = rope_interleave
195
+ self.num_key_value_heads = num_key_value_heads
196
+ self.hidden_act = hidden_act
197
+ self.initializer_range = initializer_range
198
+ self.index_topk = index_topk
199
+ self.rms_norm_eps = rms_norm_eps
200
+ self.use_cache = use_cache
201
+ self.attention_bias = attention_bias
202
+ self.attention_dropout = attention_dropout
203
+ self.rope_scaling = rope_scaling
204
+ self.rope_theta = rope_theta
205
+ self.pad_token_id = pad_token_id
206
+ self.bos_token_id = bos_token_id
207
+ self.eos_token_id = eos_token_id
208
+ self.tie_word_embeddings = tie_word_embeddings
209
+
210
+ super().__init__(**kwargs)
211
+
212
+
213
+ __all__ = ["DeepseekV32Config"]
encoding/encoding_dsv32.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict, List, Union, Optional, Tuple
2
+ import copy
3
+ import json
4
+ import re
5
+
6
+ TOOLS_SYSTEM_TEMPLATE = """## Tools
7
+
8
+ You have access to a set of tools you can use to answer the user's question.
9
+ You can invoke functions by writing a "<{dsml_token}function_calls>" block like the following as part of your reply to the user:
10
+ <{dsml_token}function_calls>
11
+ <{dsml_token}invoke name="$FUNCTION_NAME">
12
+ <{dsml_token}parameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</{dsml_token}parameter>
13
+ ...
14
+ </{dsml_token}invoke>
15
+ <{dsml_token}invoke name="$FUNCTION_NAME2">
16
+ ...
17
+ </{dsml_token}invoke>
18
+ </{dsml_token}function_calls>
19
+
20
+ String and scalar parameters should be specified as is without any escaping or quotes, while lists and objects should use JSON format. The "string" attribute should be set to "true" for string type parameters and "false" for other types (numbers, booleans, arrays, objects).
21
+
22
+ If the thinking_mode is enabled, then after function results you should strongly consider outputting a thinking block. Here is an example:
23
+
24
+ <{dsml_token}function_calls>
25
+ ...
26
+ </{dsml_token}function_calls>
27
+
28
+ <function_results>
29
+ ...
30
+ </function_results>
31
+
32
+ {thinking_start_token}...thinking about results{thinking_end_token}
33
+
34
+ Here are the functions available in JSONSchema format:
35
+ <functions>
36
+ {tool_schemas}
37
+ </functions>
38
+ """
39
+
40
+ bos_token: str = "<|begin▁of▁sentence|>"
41
+ eos_token: str = "<|end▁of▁sentence|>"
42
+ thinking_start_token: str = "<think>"
43
+ thinking_end_token: str = "</think>"
44
+ dsml_token: str = "|DSML|"
45
+ system_msg_template: str = "{content}"
46
+ user_msg_template: str = "<|User|>{content}<|Assistant|>"
47
+ assistant_msg_template: str = "{reasoning}{content}{tool_calls}<|end▁of▁sentence|>"
48
+ thinking_template = "{reasoning_content}"
49
+
50
+ response_format_template: str = (
51
+ "## Response Format:\n\nYou MUST strictly adhere to the following schema to reply:\n{schema}"
52
+ )
53
+ tool_call_template: str = (
54
+ "<{dsml_token}invoke name=\"{name}\">\n{arguments}\n</{dsml_token}invoke>"
55
+ )
56
+ tool_calls_template = (
57
+ "<{dsml_token}function_calls>\n{tool_calls}\n</{dsml_token}function_calls>"
58
+ )
59
+
60
+ tool_output_template: str = (
61
+ "\n<result>{content}</result>"
62
+ )
63
+
64
+ def to_json(value: Any) -> str:
65
+ try:
66
+ return json.dumps(value, ensure_ascii=False)
67
+ except:
68
+ return json.dumps(value, ensure_ascii=True)
69
+
70
+ def tools_from_openai_format(tools):
71
+ return [tool["function"] for tool in tools]
72
+
73
+ def tool_calls_from_openai_format(tool_calls):
74
+ return [
75
+ {
76
+ "name": tool_call["function"]["name"],
77
+ "arguments": tool_call["function"]["arguments"],
78
+ }
79
+ for tool_call in tool_calls
80
+ ]
81
+
82
+ def tool_calls_to_openai_format(tool_calls):
83
+ return [
84
+ {
85
+ "type": "function",
86
+ "function": {
87
+ "name": tool_call["name"],
88
+ "arguments": tool_call["arguments"],
89
+ }
90
+ }
91
+ for tool_call in tool_calls
92
+ ]
93
+
94
+ def encode_arguments_to_dsml(tool_call: Dict[str, str]) -> str:
95
+ p_dsml_template = """<{dsml_token}parameter name="{key}" string="{is_str}">{value}</{dsml_token}parameter>"""
96
+ P_dsml_strs = []
97
+
98
+ arguments = json.loads(tool_call["arguments"])
99
+
100
+ for k, v in arguments.items():
101
+ p_dsml_str = p_dsml_template.format(
102
+ dsml_token=dsml_token,
103
+ key=k,
104
+ is_str="true" if isinstance(v, str) else "false",
105
+ value=v if isinstance(v, str) else to_json(v),
106
+ )
107
+
108
+ P_dsml_strs.append(p_dsml_str)
109
+
110
+ return "\n".join(P_dsml_strs)
111
+
112
+
113
+ def decode_dsml_to_arguments(tool_name: str, tool_args: Dict[str, Tuple[str, str]]) -> Dict[str, str]:
114
+ def _decode_value(key: str, value: str, string: str):
115
+ if string == "true":
116
+ value = to_json(value)
117
+ return f"{to_json(key)}: {value}"
118
+
119
+ tool_args_json = "{" + ", ".join([_decode_value(k, v, string=is_str) for k, (v, is_str) in tool_args.items()]) + "}"
120
+ return dict(name=tool_name, arguments=tool_args_json)
121
+
122
+ def render_tools(tools: List[Dict[str, Union[str, Dict[str, Any]]]]) -> str:
123
+ tools_json = [to_json(t) for t in tools]
124
+
125
+ return TOOLS_SYSTEM_TEMPLATE.format(
126
+ tool_schemas="\n".join(tools_json),
127
+ dsml_token=dsml_token,
128
+ thinking_start_token=thinking_start_token,
129
+ thinking_end_token=thinking_end_token,
130
+ )
131
+
132
+ def find_last_user_index(messages: List[Dict[str, Any]]) -> int:
133
+ last_user_index = -1
134
+ for idx in range(len(messages)-1, -1, -1):
135
+ if messages[idx].get("role") in ["user", "developer"]:
136
+ last_user_index = idx
137
+ break
138
+ return last_user_index
139
+
140
+ def render_message(index: int, messages: List[Dict[str, Any]], thinking_mode: str) -> str:
141
+ assert 0 <= index < len(messages)
142
+ assert thinking_mode in ["chat", "thinking"], f"Invalid thinking_mode `{thinking_mode}`"
143
+
144
+ prompt = ""
145
+ msg = messages[index]
146
+ last_user_idx = find_last_user_index(messages)
147
+
148
+ role = msg.get("role")
149
+ content = msg.get("content")
150
+ tools = msg.get("tools")
151
+ response_format = msg.get("response_format")
152
+ tool_calls = msg.get("tool_calls")
153
+ reasoning_content = msg.get("reasoning_content")
154
+
155
+ if tools:
156
+ tools = tools_from_openai_format(tools)
157
+ if tool_calls:
158
+ tool_calls = tool_calls_from_openai_format(tool_calls)
159
+
160
+ if role == "system":
161
+ prompt += system_msg_template.format(content=content or "")
162
+ if tools:
163
+ prompt += "\n\n" + render_tools(tools)
164
+
165
+ if response_format:
166
+ prompt += "\n\n" + response_format_template.format(schema=to_json(response_format))
167
+
168
+ elif role == "developer":
169
+ assert content, f"Invalid message for role `{role}`: {msg}"
170
+ content_developer = ""
171
+ if tools:
172
+ content_developer += "\n\n" + render_tools(tools)
173
+
174
+ if response_format:
175
+ content_developer += "\n\n" + response_format_template.format(schema=to_json(response_format))
176
+
177
+ content_developer += "\n\n# The user's message is: {}".format(content)
178
+
179
+ prompt += user_msg_template.format(content=content_developer)
180
+ if index == last_user_idx and thinking_mode == "thinking":
181
+ prompt += thinking_start_token
182
+ else:
183
+ prompt += thinking_end_token
184
+
185
+ elif role == "user":
186
+ prompt += user_msg_template.format(content=content)
187
+
188
+ if index == last_user_idx and thinking_mode == "thinking":
189
+ prompt += thinking_start_token
190
+ else:
191
+ prompt += thinking_end_token
192
+
193
+ elif role == "tool":
194
+ prev_assistant_idx = index - 1
195
+ assistant_msg = messages[prev_assistant_idx]
196
+ while prev_assistant_idx >= 0 and assistant_msg.get("role") == "tool":
197
+ prev_assistant_idx -= 1
198
+ assistant_msg = messages[prev_assistant_idx]
199
+
200
+ assert index == 0 or prev_assistant_idx >= 0 and assistant_msg.get("role") == "assistant", f"Invalid messages at {index}:\n{assistant_msg}"
201
+
202
+ tool_call_order = index - prev_assistant_idx
203
+ assistant_tool_calls = assistant_msg.get("tool_calls")
204
+ assert assistant_tool_calls and len(assistant_tool_calls) >= tool_call_order, "No tool calls but found tool output"
205
+
206
+ if tool_call_order == 1:
207
+ prompt += "\n\n<function_results>"
208
+
209
+ prompt += tool_output_template.format(content=content)
210
+
211
+ if tool_call_order == len(assistant_tool_calls):
212
+ prompt += "\n</function_results>"
213
+
214
+ if index >= last_user_idx and thinking_mode == "thinking":
215
+ prompt += "\n\n" + thinking_start_token
216
+ else:
217
+ prompt += "\n\n" + thinking_end_token
218
+
219
+ elif role == "assistant":
220
+ prev_assistant_idx = index
221
+ thinking_part = ""
222
+
223
+ tool_calls_content = ""
224
+ if tool_calls:
225
+ tool_calls = [
226
+ tool_call_template.format(
227
+ dsml_token=dsml_token,
228
+ name=tool_call.get("name"),
229
+ arguments=encode_arguments_to_dsml(tool_call)
230
+ )
231
+ for tool_call in tool_calls
232
+ ]
233
+ tool_calls_content += "\n\n" + tool_calls_template.format(
234
+ dsml_token=dsml_token,
235
+ tool_calls="\n".join(tool_calls)
236
+ )
237
+
238
+ summary_content = content or ""
239
+
240
+ if thinking_mode == "thinking" and index > last_user_idx:
241
+ assert reasoning_content or tool_calls, f"ThinkingMode: {thinking_mode}, invalid message without reasoning_content/tool_calls `{msg}` after last user message"
242
+ thinking_part = thinking_template.format(reasoning_content=reasoning_content or "") + thinking_end_token
243
+
244
+ prompt += assistant_msg_template.format(
245
+ reasoning=thinking_part,
246
+ content=summary_content,
247
+ tool_calls=tool_calls_content,
248
+ )
249
+ else:
250
+ raise NotImplementedError(f"Unknown role: {role}")
251
+
252
+ return prompt
253
+
254
+ def drop_thinking_messages(messages: List[Dict[str, Any]], last_user_idx: Optional[int]=None) -> List[Dict[str, Any]]:
255
+ messages_wo_thinking: List[Dict[str, Any]] = []
256
+ last_user_idx = find_last_user_index(messages) if last_user_idx is None else last_user_idx
257
+ for idx, msg in enumerate(messages):
258
+ role = msg.get("role")
259
+ if role in ["user", "system", "tool"] or idx >= last_user_idx:
260
+ messages_wo_thinking.append(msg)
261
+ continue
262
+
263
+ elif role == "assistant":
264
+ msg_wo_thinking = copy.copy(msg)
265
+ msg_wo_thinking.pop("reasoning_content", None)
266
+ messages_wo_thinking.append(msg_wo_thinking)
267
+
268
+ return messages_wo_thinking
269
+
270
+ def encode_messages(messages: List[Dict[str, Any]], thinking_mode: str, context: Optional[List[Dict[str, Any]]] = None, drop_thinking: bool = True, add_default_bos_token: bool = True) -> str:
271
+ context = context if context else []
272
+ full_messages = context + messages
273
+
274
+ prompt = bos_token if add_default_bos_token and len(context) == 0 else ""
275
+
276
+ if thinking_mode == "thinking" and drop_thinking:
277
+ full_messages = drop_thinking_messages(full_messages)
278
+
279
+ for idx in range(len(messages)):
280
+ prompt += render_message(idx + len(context), full_messages, thinking_mode=thinking_mode)
281
+
282
+ return prompt
283
+
284
+ def _read_until_stop(index: int, text: str, stop: List[str]) -> Tuple[int, str, Optional[str]]:
285
+ min_pos = len(text)
286
+ matched_stop = None
287
+
288
+ for s in stop:
289
+ pos = text.find(s, index)
290
+ if pos != -1 and pos < min_pos:
291
+ min_pos = pos
292
+ matched_stop = s
293
+
294
+ if matched_stop:
295
+ content = text[index:min_pos]
296
+ return min_pos + len(matched_stop), content, matched_stop
297
+ else:
298
+ content = text[index:]
299
+ return len(text), content, None
300
+
301
+ def parse_tool_calls(index: int, text: str):
302
+ tool_calls: List[Dict[str, Any]] = []
303
+ stop_token = None
304
+ tool_calls_end_token = f"</{dsml_token}function_calls>"
305
+
306
+ while index < len(text):
307
+ index, _, stop_token = _read_until_stop(index, text, [f"<{dsml_token}invoke", tool_calls_end_token])
308
+ assert _ == ">\n", "Tool call format error"
309
+
310
+ if stop_token == tool_calls_end_token:
311
+ break
312
+
313
+ assert stop_token is not None, "Missing special token"
314
+
315
+ index, tool_name_content, stop_token = _read_until_stop(index, text, [f"<{dsml_token}parameter", f"</{dsml_token}invoke"])
316
+
317
+ p_tool_name = re.findall(r'^\s*name="(.*?)">\n$', tool_name_content, flags=re.DOTALL)
318
+ assert len(p_tool_name) == 1, "Tool name format error"
319
+ tool_name = p_tool_name[0]
320
+
321
+ tool_args: Dict[str, Tuple[str, str]] = {}
322
+ while stop_token == f"<{dsml_token}parameter":
323
+ index, param_content, stop_token = _read_until_stop(index, text, [f"/{dsml_token}parameter"])
324
+
325
+ param_kv = re.findall(r'^ name="(.*?)" string="(true|false)">(.*?)<$', param_content, flags=re.DOTALL)
326
+ assert len(param_kv) == 1, "Parameter format error"
327
+ param_name, string, param_value = param_kv[0]
328
+
329
+ assert param_name not in tool_args, "Duplicate parameter name"
330
+ tool_args[param_name] = (param_value, string)
331
+
332
+ index, content, stop_token = _read_until_stop(index, text, [f"<{dsml_token}parameter", f"</{dsml_token}invoke"])
333
+ assert content == ">\n", "Parameter format error"
334
+
335
+ tool_call = decode_dsml_to_arguments(tool_name=tool_name, tool_args=tool_args)
336
+ tool_calls.append(tool_call)
337
+
338
+ return index, stop_token, tool_calls
339
+
340
+ # NOTE: This function is designed to parse only correctly formatted string and will not attempt to correct malformed output that may be generated by the model.
341
+ def parse_message_from_completion_text(text: str, thinking_mode: str):
342
+ summary_content, reasoning_content, tool_calls = "", "", []
343
+ index, stop_token = 0, None
344
+ tool_calls_start_token = f"\n\n<{dsml_token}function_calls"
345
+
346
+ is_thinking, is_tool_calling = thinking_mode == "thinking", False
347
+
348
+ if is_thinking:
349
+ index, content_delta, stop_token = _read_until_stop(index, text, [thinking_end_token, tool_calls_start_token])
350
+ reasoning_content = content_delta
351
+ assert stop_token == thinking_end_token, "Invalid thinking format"
352
+
353
+ index, content_delta, stop_token = _read_until_stop(index, text, [eos_token, tool_calls_start_token])
354
+ summary_content = content_delta
355
+ if stop_token == tool_calls_start_token:
356
+ is_tool_calling = True
357
+ else:
358
+ assert stop_token == eos_token, "Invalid summary format"
359
+
360
+ if is_tool_calling:
361
+ index, stop_token, tool_calls = parse_tool_calls(index, text)
362
+
363
+ index, tool_ends_text, stop_token = _read_until_stop(index, text, [eos_token])
364
+ assert not tool_ends_text, "Unexpected content after tool calls"
365
+
366
+ assert len(text) == index and stop_token in [eos_token, None], "Unexpected content at end"
367
+
368
+ for sp_token in [bos_token, eos_token, thinking_start_token, thinking_end_token, dsml_token]:
369
+ assert sp_token not in summary_content and sp_token not in reasoning_content, "Unexpected special token in content"
370
+
371
+ return {
372
+ "role": "assistant",
373
+ "content": summary_content,
374
+ "reasoning_content": reasoning_content,
375
+ "tool_calls": tool_calls_to_openai_format(tool_calls)
376
+ }
encoding/test_encoding_dsv32.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import copy
3
+
4
+ from encoding_dsv32 import encode_messages, parse_message_from_completion_text
5
+
6
+ with open("test_input.json", "r") as f:
7
+ test_dict = json.load(f)
8
+ messages = test_dict["messages"]
9
+ messages[0]["tools"] = test_dict["tools"]
10
+
11
+ with open("test_output.txt", "r") as f:
12
+ gold_prompt = f.read().strip()
13
+
14
+ print(messages)
15
+ print("=" * 60)
16
+
17
+ encode_config = dict(thinking_mode="thinking", drop_thinking=True, add_default_bos_token=True)
18
+ prompt = encode_messages(messages, **encode_config)
19
+ print(prompt)
20
+ assert prompt == gold_prompt
21
+ print("=" * 60)
22
+
23
+ tool_call_message = messages[4]
24
+ tool_call_prompt = encode_messages([tool_call_message], context=messages[:4], **encode_config)
25
+ tool_call_message_wo_id = copy.deepcopy(tool_call_message)
26
+ for tool_call in tool_call_message_wo_id["tool_calls"]:
27
+ tool_call.pop("id")
28
+ parsed_tool_call_message = parse_message_from_completion_text(tool_call_prompt, thinking_mode="thinking")
29
+ parsed_tool_call_message.pop("content")
30
+ assert tool_call_message_wo_id == parsed_tool_call_message
31
+
32
+ thinking_message = messages[-6]
33
+ thinking_prompt = encode_messages([thinking_message], context=messages[:-6], **encode_config)
34
+ parsed_thinking_message = parse_message_from_completion_text(thinking_prompt, thinking_mode="thinking")
35
+ parsed_thinking_message.pop("tool_calls")
36
+ assert thinking_message == parsed_thinking_message
37
+
38
+ with open("test_input_search_wo_date.json", "r") as f:
39
+ search_messages = json.load(f)["messages"]
40
+
41
+ with open("test_output_search_wo_date.txt", "r") as f:
42
+ search_gold_prompt = f.read().strip()
43
+
44
+ search_prompt = encode_messages(search_messages, **encode_config)
45
+ assert search_prompt == search_gold_prompt
46
+
47
+ with open("test_input_search_w_date.json", "r") as f:
48
+ search_messages_w_date = json.load(f)["messages"]
49
+
50
+ with open("test_output_search_w_date.txt", "r") as f:
51
+ search_gold_prompt_w_date = f.read().strip()
52
+
53
+ search_prompt_w_date = encode_messages(search_messages_w_date, **encode_config)
54
+ with open("test_output_search_w_date_2.txt", "w") as f:
55
+ f.write(search_prompt_w_date)
56
+ assert search_prompt_w_date == search_gold_prompt_w_date
encoding/test_input.json ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tools": [
3
+ {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "get_datetime",
7
+ "description": "Get the current date and time",
8
+ "parameters": {
9
+ "type": "object",
10
+ "properties": {
11
+ "timezone": {
12
+ "type": "string",
13
+ "description": "The timezone, e.g. Asia/Shanghai, UTC"
14
+ }
15
+ },
16
+ "required": ["timezone"]
17
+ }
18
+ }
19
+ },
20
+ {
21
+ "type": "function",
22
+ "function": {
23
+ "name": "get_weather",
24
+ "description": "Get the weather for a specific date and location",
25
+ "parameters": {
26
+ "type": "object",
27
+ "properties": {
28
+ "location": {
29
+ "type": "string",
30
+ "description": "The city name, e.g. Beijing, Hangzhou"
31
+ },
32
+ "date": {
33
+ "type": "string",
34
+ "description": "The date in YYYY-MM-DD format"
35
+ }
36
+ },
37
+ "required": ["location", "date"]
38
+ }
39
+ }
40
+ }
41
+ ],
42
+ "messages": [
43
+ {
44
+ "role": "system",
45
+ "content": "You are a helpful Assistant."
46
+ },
47
+ {
48
+ "role": "user",
49
+ "content": "明天杭州和北京的天气怎么样?"
50
+ },
51
+ {
52
+ "role": "assistant",
53
+ "reasoning_content": "用户询问明天的天气,我需要先获取当前日期来计算明天的日期📅",
54
+ "tool_calls": [
55
+ {
56
+ "id": "call_xK9mN3pL2qR8vT5wY6hZ1aB4",
57
+ "type": "function",
58
+ "function": {
59
+ "arguments": "{\"timezone\": \"Asia/Shanghai\"}",
60
+ "name": "get_datetime"
61
+ }
62
+ }
63
+ ]
64
+ },
65
+ {
66
+ "tool_call_id": "call_xK9mN3pL2qR8vT5wY6hZ1aB4",
67
+ "role": "tool",
68
+ "content": "{\"current_date\": \"2024-01-15\", \"current_time\": \"14:30:00\", \"timezone\": \"Asia/Shanghai\"}"
69
+ },
70
+ {
71
+ "role": "assistant",
72
+ "reasoning_content": "现在知道今天是2024-01-15,明天就是2024-01-16。接下来查询杭州和北京明天的天气🌤️",
73
+ "tool_calls": [
74
+ {
75
+ "id": "call_bN7kR9mX3pQ2wL5vY8jZ4cD6",
76
+ "type": "function",
77
+ "function": {
78
+ "arguments": "{\"location\": \"Hangzhou\", \"date\": \"2024-01-16\"}",
79
+ "name": "get_weather"
80
+ }
81
+ },
82
+ {
83
+ "id": "call_dP9mL7kX5rT4yN3wZ2hV8eF1",
84
+ "type": "function",
85
+ "function": {
86
+ "arguments": "{\"location\": \"Beijing\", \"date\": \"2024-01-16\"}",
87
+ "name": "get_weather"
88
+ }
89
+ }
90
+ ]
91
+ },
92
+ {
93
+ "tool_call_id": "call_bN7kR9mX3pQ2wL5vY8jZ4cD6",
94
+ "role": "tool",
95
+ "content": "{\"location\": \"Hangzhou\", \"date\": \"2024-01-16\", \"temperature_high\": \"12\", \"temperature_low\": \"5\", \"weather\": \"多云\", \"humidity\": \"65%\"}"
96
+ },
97
+ {
98
+ "tool_call_id": "call_dP9mL7kX5rT4yN3wZ2hV8eF1",
99
+ "role": "tool",
100
+ "content": "{\"location\": \"Beijing\", \"date\": \"2024-01-16\", \"temperature_high\": \"-2\", \"temperature_low\": \"-8\", \"weather\": \"晴\", \"humidity\": \"30%\"}"
101
+ },
102
+ {
103
+ "role": "assistant",
104
+ "reasoning_content": "已获取两个城市明天的天气信息,现在整理给用户✨",
105
+ "content": "根据查询结果,明天(2024年1月16日)的天气情况如下:\n\n**杭州**:\n- 天气:多云\n- 最高温度:12°C\n- 最低温度:5°C\n- 湿度:65%\n\n**北京**:\n- 天气:晴\n- 最高温度:-2°C\n- 最低温度:-8°C\n- 湿度:30%\n\n杭州明天会比较温暖但有些多云,而北京会很冷但是晴天。建议在北京的朋友要注意保暖!"
106
+ },
107
+ {
108
+ "role": "user",
109
+ "content": "谢谢!那后天呢?"
110
+ },
111
+ {
112
+ "role": "assistant",
113
+ "reasoning_content": "用户现在问后天的天气,后天是2024-01-17,我可以直接查询(因为已知今天日期)🗓️",
114
+ "tool_calls": [
115
+ {
116
+ "id": "call_fR3nK8mV7pL4xT2yW9jB5gH3",
117
+ "type": "function",
118
+ "function": {
119
+ "arguments": "{\"location\": \"Hangzhou\", \"date\": \"2024-01-17\"}",
120
+ "name": "get_weather"
121
+ }
122
+ },
123
+ {
124
+ "id": "call_hT5pN2kY9rV6zL3wX1mD7jK8",
125
+ "type": "function",
126
+ "function": {
127
+ "arguments": "{\"location\": \"Beijing\", \"date\": \"2024-01-17\"}",
128
+ "name": "get_weather"
129
+ }
130
+ }
131
+ ]
132
+ },
133
+ {
134
+ "tool_call_id": "call_fR3nK8mV7pL4xT2yW9jB5gH3",
135
+ "role": "tool",
136
+ "content": "{\"location\": \"Hangzhou\", \"date\": \"2024-01-17\", \"temperature_high\": \"15\", \"temperature_low\": \"8\", \"weather\": \"小雨\", \"humidity\": \"80%\"}"
137
+ },
138
+ {
139
+ "tool_call_id": "call_hT5pN2kY9rV6zL3wX1mD7jK8",
140
+ "role": "tool",
141
+ "content": "{\"location\": \"Beijing\", \"date\": \"2024-01-17\", \"temperature_high\": \"0\", \"temperature_low\": \"-6\", \"weather\": \"多云\", \"humidity\": \"45%\"}"
142
+ },
143
+ {
144
+ "role": "assistant",
145
+ "reasoning_content": "获取到后天的天气数据,整理回复给用户📝",
146
+ "content": "后天(2024年1月17日)的天气情况:\n\n**杭州**:\n- 天气:小雨\n- 最高温度:15°C\n- 最低温度:8°C\n- 湿度:80%\n\n**北京**:\n- 天气:多云\n- 最高温度:0°C\n- 最低温度:-6°C\n- 湿度:45%\n\n杭州后天会有小雨,温度略有回升,记得带伞。北京会稍微暖和一点,但依然很冷,请继续做好保暖措施。"
147
+ }
148
+ ]
149
+ }
encoding/test_input_search_w_date.json ADDED
The diff for this file is too large to render. See raw diff
 
encoding/test_input_search_wo_date.json ADDED
@@ -0,0 +1,533 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "messages": [
3
+ {
4
+ "role": "developer",
5
+ "content": "In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles.",
6
+ "tools": [
7
+ {
8
+ "type": "function",
9
+ "function": {
10
+ "name": "search",
11
+ "description": "Searches for information related to query and displays topn results.",
12
+ "parameters": {
13
+ "type": "object",
14
+ "properties": {
15
+ "query": {
16
+ "type": "string",
17
+ "description": "The search query string"
18
+ },
19
+ "topn": {
20
+ "type": "integer",
21
+ "description": "Number of top results to display",
22
+ "default": 10
23
+ },
24
+ "source": {
25
+ "type": "string",
26
+ "description": "Source to search within",
27
+ "enum": [
28
+ "web",
29
+ "news"
30
+ ],
31
+ "default": "web"
32
+ }
33
+ },
34
+ "required": [
35
+ "query"
36
+ ],
37
+ "additionalProperties": false,
38
+ "$schema": "http://json-schema.org/draft-07/schema#"
39
+ }
40
+ }
41
+ },
42
+ {
43
+ "type": "function",
44
+ "function": {
45
+ "name": "open",
46
+ "description": "Opens the link id from the page indicated by cursor starting at line number loc, showing num_lines lines. Valid link ids are displayed with the formatting: 【{id}†.*】. If cursor is not provided, the most recent page is implied. If id is a string, it is treated as a fully qualified URL associated with source. If loc is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available. Use this function without id to scroll to a new location of an opened page.",
47
+ "parameters": {
48
+ "type": "object",
49
+ "properties": {
50
+ "id": {
51
+ "description": "Link ID (number) or fully qualified URL (string)",
52
+ "anyOf": [
53
+ {
54
+ "type": "integer"
55
+ },
56
+ {
57
+ "type": "string"
58
+ }
59
+ ],
60
+ "default": -1
61
+ },
62
+ "cursor": {
63
+ "type": "integer",
64
+ "description": "Page cursor indicator",
65
+ "default": -1
66
+ },
67
+ "loc": {
68
+ "type": "integer",
69
+ "description": "Starting line number",
70
+ "default": -1
71
+ },
72
+ "num_lines": {
73
+ "type": "integer",
74
+ "description": "Number of lines to show",
75
+ "default": -1
76
+ },
77
+ "view_source": {
78
+ "type": "boolean",
79
+ "description": "Whether to view source",
80
+ "default": false
81
+ },
82
+ "source": {
83
+ "type": "string",
84
+ "description": "Source associated with the URL",
85
+ "enum": [
86
+ "web",
87
+ "news"
88
+ ],
89
+ "default": "web"
90
+ }
91
+ },
92
+ "additionalProperties": false,
93
+ "$schema": "http://json-schema.org/draft-07/schema#"
94
+ }
95
+ }
96
+ },
97
+ {
98
+ "type": "function",
99
+ "function": {
100
+ "name": "find",
101
+ "description": "Finds exact matches of pattern in the current page, or the page given by cursor.",
102
+ "parameters": {
103
+ "type": "object",
104
+ "properties": {
105
+ "pattern": {
106
+ "type": "string",
107
+ "description": "The exact pattern to search for"
108
+ },
109
+ "cursor": {
110
+ "type": "integer",
111
+ "description": "Page cursor to search in",
112
+ "default": -1
113
+ }
114
+ },
115
+ "required": [
116
+ "pattern"
117
+ ],
118
+ "additionalProperties": false,
119
+ "$schema": "http://json-schema.org/draft-07/schema#"
120
+ }
121
+ }
122
+ },
123
+ {
124
+ "type": "function",
125
+ "function": {
126
+ "name": "python",
127
+ "description": "Use this tool to execute Python code. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files). When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 120.0 seconds.",
128
+ "parameters": {
129
+ "type": "object",
130
+ "properties": {
131
+ "code": {
132
+ "type": "string",
133
+ "description": "The python code to execute in the jupyter environment."
134
+ }
135
+ },
136
+ "required": [
137
+ "code"
138
+ ],
139
+ "additionalProperties": false,
140
+ "$schema": "http://json-schema.org/draft-07/schema#"
141
+ }
142
+ }
143
+ }
144
+ ]
145
+ },
146
+ {
147
+ "role": "assistant",
148
+ "content": "",
149
+ "reasoning_content": "The user asks: \"In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles.\"\n\nWe need to parse the puzzle.\n\nWe have clues about a press release about vaccine doses for a country, dated between Dec 1-15 inclusive, reporting as of 5th of that month total doses received 16,201,670 and administered 7,583,134. That is likely a COVID-19 vaccine update for some country. Which country? Might be a smaller country. Could be something like \"Uganda\", \"Zimbabwe\", \"Kenya\", \"Philippines\", etc. Let's search.\n\nSearch for \"16,201,670 vaccine doses\" \"7,583,134 administered\".",
150
+ "tool_calls": [
151
+ {
152
+ "id": null,
153
+ "type": "function",
154
+ "function": {
155
+ "name": "search",
156
+ "arguments": "{\"query\": \"\\\"16,201,670\\\" vaccine doses\", \"topn\": 10, \"source\": \"web\"}"
157
+ }
158
+ }
159
+ ]
160
+ },
161
+ {
162
+ "role": "tool",
163
+ "content": "[0] \"16,201,670\" vaccine doses (Search_Results/\"16,201,670\" vaccine doses)\n**viewing lines [0 - 38] of 38**\n\nL0: \nL1: URL: Search_Results/\"16,201,670\" vaccine doses\nL2: # Search Results\nL3: \nL4: * 【0†Kenya's Economy is Showing Resilience as Output Rises ...; publish_date: \nL5: none†www.worldbank.org】 Dec 14, 2021 — As of December 5, 2021, Kenya had \nL6: received a total of 16,201,670 vaccines, with 7,583,134 administered. While \nL7: vaccine acceptance is ...\nL8: * 【1†MINISTRY OF HEALTH; publish_date: none†covidhub.mediacouncil.or.ke】 Dec \nL9: 1, 2021 — Total Doses Received 16,201,670. Table 10 gives the total vaccines \nL10: received since the start of Covid -19 vaccination exercise in the country.\nL11: * 【2†Output Result Page; publish_date: none†open.unicef.org】 ... 16,201,670 \nL12: doses of multiple vaccines nationwide and full vaccination of 15.5 per cent with\nL13: two doses of COVID-19 vaccine as of 31 December 2021.\nL14: * 【3†rebased GDP; publish_date: none†documents1.worldbank.org】 Dec 7, 2021 — \nL15: As of December 5, 2021,. Kenya had received a total of 16,201,670 vaccines, \nL16: with. 7,583,134 administered. Vaccine acceptance is reportedly high.\nL17: * 【4†Integrated Annual Report; publish_date: none†www.co-opbank.co.ke】 May 27,\nL18: 2022 — ... doses of Covid-19 vaccines and administered close to 17 million ... \nL19: 16,201,670, huku 7,583,134 zikiwa tayari zimedungwa watu. Bado kuna ...\nL20: * 【5†World Bank lifts Kenya's growth prospect to 5% in 2021; publish_date: \nL21: none†www.africa-press.net】 ... 16,201,670 vaccines, with 7,583,134 administered.\nL22: While vaccine acceptance is reportedly high there is still a long way to go \nL23: towards the government's ...\nL24: * 【6†2020 Annual Report United States; publish_date: none†www.hi-us.org】 \nL25: including medicine, vaccines, Covid-19 prevention kits, and kits of ... \nL26: 16,201,670. 9,630,584. Prepaid expenses. 56,707. 22,601. Total current assets.\nL27: * 【7†HOSPITAL CENTRAL DE LA DEFENSA; publish_date: \nL28: none†contrataciondelestado.es】 16201670 X. URPA-URPA. 0. HCD-000393/00. KONTRON.\nL29: 7000 ... LASER SHOT LBP-1120. CNEUR-C. NEUROFISIOLOGÍA. 0 ... VICTOREEN CT HEAD\nL30: DOSE PHANTON. 43650-014.\nL31: * 【8†body metal concentrations: Topics by ...; publish_date: \nL32: none†www.science.gov】 PMID:16201670. Design Characteristics and Tobacco Metal \nL33: Concentrations in Filtered Cigars. PubMed. Caruso, Rosalie V; O'Connor, Richard \nL34: J; Travers, Mark J ...\nL35: * 【9†Public Offices, Personal Demands: Capability in ...; publish_date: \nL36: none†dokumen.pub】 ... (16201670).9 The Institutiones Politicae consists of two \nL37: books. Book one deals with the origin, nature and goal of the commonwealth on \nL38: the one hand, and the ...",
164
+ "tool_call_id": null
165
+ },
166
+ {
167
+ "role": "assistant",
168
+ "content": "",
169
+ "reasoning_content": "Result shows Kenya. So the press release is about Kenya's vaccine doses as of Dec 5, 2021. The press release likely by World Bank or something. The search result shows \"World Bank lifts Kenya's growth prospect...\" maybe a press release dated Dec 14, 2021. But the clue says \"between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered.\" That matches Kenya on Dec 5, 2021. So the year is 2021.\n\nNow the next clue: \"That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services.\"\n\nThis sounds like an environmental or conservation organization. \"Nearly 2.2 million members and supporters contributed over $49 million.\" That might be something like \"The Nature Conservancy\", \"World Wildlife Fund\", \"Sierra Club\", \"Environmental Defense Fund\", \"Natural Resources Defense Council\", etc. Let's search.\n\nSearch for \"nearly 2.2 million members and supporters contributed over $49 million\" \"12% of total revenue coming from contributed services\".",
170
+ "tool_calls": [
171
+ {
172
+ "id": null,
173
+ "type": "function",
174
+ "function": {
175
+ "name": "search",
176
+ "arguments": "{\"query\": \"\\\"nearly 2.2 million members\\\" \\\"contributed over $49 million\\\"\", \"topn\": 10, \"source\": \"web\"}"
177
+ }
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "role": "tool",
183
+ "content": "[1] \"nearly 2.2 million members\" \"contributed over $49 million\" (Search_Results/\"nearly 2.2 million members\" \"contributed over $49 million\")\n**viewing lines [0 - 41] of 41**\n\nL0: \nL1: URL: Search_Results/\"nearly 2.2 million members\" \"contributed over $49 million\"\nL2: # Search Results\nL3: \nL4: * 【0†The Global Good Fund, Microsoft and Goodwill join forces ...; \nL5: publish_date: none†globalgoodfund.org】 Sep 28, 2022 — ... more than 2.2 million \nL6: people. They additionally contribute close to $49 billion in local, state and \nL7: federal tax revenues. When COVID hit ...\nL8: * 【1†Almost 22 billion American tax dollars spent to wipe out a ...; \nL9: publish_date: none†www.facebook.com】 US military funding for Israel's war crimes\nL10: in Lebanon and Gaza has now cost US taxpayers over $22 billion. When millions \nL11: struggle to afford the ...\nL12: * 【2†Corporate America has largely abandoned its post-January ...; \nL13: publish_date: none†www.citizensforethics.org】 Jul 29, 2025 — Since the January 6\nL14: insurrection, over 2,000 corporate and industry group PACs have given over $174\nL15: million to members of the Sedition ...\nL16: * 【3†Audit shows millions in questionable taxpayer spending at ...; \nL17: publish_date: none†www.aol.com】 18 hours ago — ... nearly doubled from 1.3 \nL18: million to about 2.2 million. That is more than one in four Washington state \nL19: residents receiving Medicaid, and the ...\nL20: * 【4†Incarceration and Poverty in the United States - AAF; publish_date: \nL21: none†www.americanactionforum.org】 Jun 30, 2020 — The United States currently \nL22: incarcerates 2.2 million people, nearly half of whom are non-violent drug \nL23: offenders, accused people held pre ...\nL24: * 【5†How the NRA Spends Revenue (2023); publish_date: none†paddockpost.com】 \nL25: Apr 25, 2025 — In 2023, the NRA reported total revenue of $176 million (compared\nL26: to $211 million in 2022, $227 million in 2021, $282 million in 2020, $292 ...\nL27: * 【6†NCA Newsroom; publish_date: none†www.ncausa.org】 ... 2.2 million U.S. \nL28: jobs—operating in every U.S. state and territory and contributing nearly $350 \nL29: billion to the U.S. economy every year. For media inquiries ...\nL30: * 【7†The Early Childhood Educator Workforce; publish_date: \nL31: none†cscce.berkeley.edu】 About 2.2 million adults are paid to care for and \nL32: educate more than 9.7 million children between birth and age five in center-\nL33: based and home-based settings.\nL34: * 【8†Bringing Out the Worst in Congress: CRA By the Numbers ...; publish_date:\nL35: none†progressivereform.org】 Jul 14, 2025 — In fact, the top ten recipients (out\nL36: of 25) account for almost $7 million, showing how these contributions are \nL37: targeted and strategic. Real ...\nL38: * 【9†Club 100: Over 100 Americans gave more than $100000 to ...; publish_date:\nL39: none†publicintegrity.org】 Jan 7, 2022 — Through Internet investments during the\nL40: 1990s, he attracted 2.2 million online accounts valued at $174 million. Schwab \nL41: has been a vocal ...",
184
+ "tool_call_id": null
185
+ },
186
+ {
187
+ "role": "assistant",
188
+ "content": "",
189
+ "reasoning_content": "Not that. Try search for \"2.2 million members and supporters contributed over $49 million\".",
190
+ "tool_calls": [
191
+ {
192
+ "id": null,
193
+ "type": "function",
194
+ "function": {
195
+ "name": "search",
196
+ "arguments": "{\"query\": \"\\\"2.2 million members\\\" \\\"contributed over $49\\\"\", \"topn\": 10, \"source\": \"web\"}"
197
+ }
198
+ }
199
+ ]
200
+ },
201
+ {
202
+ "role": "tool",
203
+ "content": "[2] \"2.2 million members\" \"contributed over $49\" (Search_Results/\"2.2 million members\" \"contributed over $49\")\n**viewing lines [0 - 2] of 2**\n\nL0: \nL1: URL: Search_Results/\"2.2 million members\" \"contributed over $49\"\nL2: # Search Results",
204
+ "tool_call_id": null
205
+ },
206
+ {
207
+ "role": "assistant",
208
+ "content": "",
209
+ "reasoning_content": "Maybe it's an annual report of a non-profit. Search for \"annual report 2.2 million members contributed $49 million\".",
210
+ "tool_calls": [
211
+ {
212
+ "id": null,
213
+ "type": "function",
214
+ "function": {
215
+ "name": "search",
216
+ "arguments": "{\"query\": \"\\\"2.2 million members\\\" \\\"annual report\\\" $49 million\", \"topn\": 10, \"source\": \"web\"}"
217
+ }
218
+ }
219
+ ]
220
+ },
221
+ {
222
+ "role": "tool",
223
+ "content": "[3] \"2.2 million members\" \"annual report\" $49 million (Search_Results/\"2.2 million members\" \"annual report\" $49 million)\n**viewing lines [0 - 36] of 36**\n\nL0: \nL1: URL: Search_Results/\"2.2 million members\" \"annual report\" $49 million\nL2: # Search Results\nL3: \nL4: * 【0†20-F; publish_date: none†www.sec.gov】 ANNUAL REPORT PURSUANT TO SECTION \nL5: ... Our membership grew from 2.1 million members as at December 31, 2023 to 2.2 \nL6: million members as at December 31, 2024.\nL7: * 【1†Oportun Reports Fourth Quarter and Full Year 2023 ...; publish_date: \nL8: none†investor.oportun.com】 Mar 12, 2024 — Oportun (Nasdaq: OPRT) is a mission-\nL9: driven fintech that puts its 2.2 million members' financial goals within reach. \nL10: ... annual report on ...\nL11: * 【2†2 0 21; publish_date: none†www.annualreports.com】 ANNUAL REPORT. 2. 0. \nL12: 21. 2. 0. 21. Page 2. 2. DEFENDERS OF WILDLIFE. 2. 0. 21. 2. 0. 21 ... In 2021, \nL13: Defenders of Wildlife's nearly 2.2 million members and.\nL14: * 【3†Annual report and accounts 2020; publish_date: none†www.3i.com】 \nL15: Disclaimer. The Annual report and accounts have been prepared solely to provide \nL16: information to shareholders. ... 2.2 million members. In December 2019, we sold \nL17: ...\nL18: * 【4†united states securities and exchange commission; publish_date: \nL19: none†s21.q4cdn.com】 Dec 14, 2018 — ☒ ANNUAL REPORT ... approximately 5.7 million\nL20: Members, including approximately 2.2 million Members from the acquisition of \nL21: Best Doctors.\nL22: * 【5†securities and exchange commission; publish_date: none†www.sec.gov】 ... \nL23: Annual Report on Form 10-K filed with the SEC on March 10, 2020. The ... 2.2 \nL24: million members at June 30, 2020. The following table highlights the ...\nL25: * 【6†2022-23 Annual Comprehensive Financial Report - CalPERS; publish_date: \nL26: none†www.calpers.ca.gov】 Nov 17, 2023 — ... annual report is consistent with \nL27: these accounting principles ... 2.2 million members and health benefit services \nL28: to over 1.5 million ...\nL29: * 【7†Medical Centers Report; publish_date: \nL30: none†regents.universityofcalifornia.edu】 Oct 8, 2019 — which will allow its 2.2 \nL31: million members to receive advanced and ... annual report of the University of \nL32: California. For the Medical ...\nL33: * 【8†Working TogeTher to make HealtH care better; publish_date: \nL34: none†www.unitedhealthgroup.com】 Feb 8, 2012 — ANNUAL REPORT PURSUANT TO SECTION \nL35: 13 OR 15(d) OF THE ... approximately 2.2 million members enrolled in its \nL36: Medicare. Advantage ...",
224
+ "tool_call_id": null
225
+ },
226
+ {
227
+ "role": "assistant",
228
+ "content": "",
229
+ "reasoning_content": "Result 2 shows \"Defenders of Wildlife\". That might be the organization. Let's open.",
230
+ "tool_calls": [
231
+ {
232
+ "id": null,
233
+ "type": "function",
234
+ "function": {
235
+ "name": "open",
236
+ "arguments": "{\"cursor\": 3, \"id\": 2}"
237
+ }
238
+ }
239
+ ]
240
+ },
241
+ {
242
+ "role": "tool",
243
+ "content": "[4] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)\n**viewing lines [0 - 53] of 261**\n\nL0: \nL1: URL: https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-\nL2: wildlife_2021.pdf\nL3: ===== Page 1 ===== 2021 ANNUAL REPORT ===== Page 2 ===== # DEFENDERS OF WILDLIFE\nL4: made important progress for imperiled species and vital landscapes across the \nL5: United States in 2021. \\--- **LAWYERS** immediately **challenged** the premature\nL6: and reckless decision to strip **gray wolves** of federal **Endangered Species \nL7: Act (ESA)** protections. For many decades, Defenders has led the effort to \nL8: protect and restore the gray wolf, and we will continue to fight the \nL9: unscientific and hostile anti-wolf policies that impede conservation progress \nL10: and will carry on our unrelenting battle to restore federal protections for this\nL11: iconic keystone species. \\--- **LOBBYISTS** worked around the clock to keep \nL12: wildlife and climate priorities in the **Infrastructure Investment and Jobs \nL13: Act**. We also continue fighting to keep important wildlife and habitat funding \nL14: in relevant **appropriations bills**. \\--- 2 DEFENDERS OF WILDLIFE ===== Page 3 \nL15: ===== POLICY EXPERTS pushed forward on the urgent need for a National \nL16: Biodiversity Strategy (NBS), an all-of-government approach to address the \nL17: unprecedented loss of wildlife and habitat we are experiencing. We have coupled \nL18: this with our new campaign to expand the National Wildlife Refuge System to \nL19: preserve our nation’s only lands set aside for wildlife. By defending, funding \nL20: and expanding our national wildlife refuges, we will directly address \nL21: biodiversity loss and climate change while promoting increased equitable access \nL22: to nature. FIELD TEAMS were on the ground helping to recover imperiled species. \nL23: From panthers and sea turtles in Florida to wolves, bison and black-footed \nL24: ferrets in Montana, Defenders’ conservation experts were in the field saving \nL25: wildlife all over the country. CONSERVATION INNOVATION EXPERTS provided \nL26: comprehensive analyses to guide policy and inform conservation strategies to \nL27: reach the goal of protecting 30% of our terrestrial and marine systems by 2030 \nL28: (“30x30”). Defenders’ Center for Conservation Innovation (CCI) produced a report\nL29: which details actions we need to take to achieve 30x30 while protecting \nL30: biodiversity and addressing the climate crisis. DEFENDERS.ORG ===== Page 4 =====\nL31: WE HAVE ACCOMPLISHED MUCH THIS YEAR WORKING WITH AN ADMINISTRATION THAT VALUES \nL32: SCIENCE AND CONSERVATION. Our many successes include the return of protections \nL33: to the Tongass National Forest in Alaska and the suspension of oil and gas \nL34: leasing permits for the Arctic National Wildlife Refuge. Defenders also played a\nL35: lead role in successfully defending the Migratory Bird Protection Act from \nL36: attack and securing critical protections for migratory birds. Throughout 2021 \nL37: Defenders made important progress for the wildlife and wild places we all love, \nL38: yet our nation’s wildlife still face unprecedented challenges. More and more \nL39: species, including manatees, Mexican gray wolves, polar bears and North Atlantic\nL40: right whales, face the very real threat of extinction because of climate \nL41: change, habitat loss, pollution and inadequate protections. In our work we \nL42: continue to demonstrate success by following the science, building partnerships,\nL43: leading with innovative solutions and focusing on collaborative and practical \nL44: approaches in defense of nature. Together we can make a lasting difference for \nL45: wildlife and habitats. Your support is essential to our work and we couldn’t do \nL46: it without you. Thank you for being an important part of our efforts to protect \nL47: imperiled wildlife across our nation. Jamie Rappaport Clark President and CEO \nL48: John Dayton Board Chair OUR NEW TELEVISION SERIES, WILDLIFE NATION WITH JEFF \nL49: CORWIN, premiered in October and airs every weekend on ABC stations nationwide. \nL50: This groundbreaking series takes Jeff across the country to meet Defenders’ \nL51: experts along with our science, tribal and community partners to educate and \nL52: inspire viewers to join with Defenders to protect wildlife. While highlighting \nL53: some of our important conservation work and raising awareness of important ",
244
+ "tool_call_id": null
245
+ },
246
+ {
247
+ "role": "assistant",
248
+ "content": "",
249
+ "reasoning_content": "Search within PDF for \"2.2 million members\".",
250
+ "tool_calls": [
251
+ {
252
+ "id": null,
253
+ "type": "function",
254
+ "function": {
255
+ "name": "find",
256
+ "arguments": "{\"cursor\": 4, \"pattern\": \"2.2\"}"
257
+ }
258
+ }
259
+ ]
260
+ },
261
+ {
262
+ "role": "tool",
263
+ "content": "[5] Find results for text: `2.2` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf/find?pattern=2.2)\n**viewing lines [0 - 4] of 4**\n\nL0: # 【0†match at L187】\nL1: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters \nL2: contributed more than $49 million for wildlife and wildlife habitat. This figure\nL3: includes all those who generously donated their time and expertise. The \nL4: steadfast support of our donors allows Defenders to sustain our program and ",
264
+ "tool_call_id": null
265
+ },
266
+ {
267
+ "role": "assistant",
268
+ "content": "",
269
+ "reasoning_content": "Open around L180.",
270
+ "tool_calls": [
271
+ {
272
+ "id": null,
273
+ "type": "function",
274
+ "function": {
275
+ "name": "open",
276
+ "arguments": "{\"cursor\": 4, \"loc\": 170}"
277
+ }
278
+ }
279
+ ]
280
+ },
281
+ {
282
+ "role": "tool",
283
+ "content": "[6] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)\n**viewing lines [170 - 215] of 261**\n\nL170: into the wild. **10 DEFENDERS OF WILDLIFE** ===== Page 11 ===== AFTER ANOTHER \nL171: SUCCESSFUL DEFENDERS LAWSUIT ON BEHALF OF THE CRITICALLY ENDANGERED RED WOLF, \nL172: FWS reversed its decision to limit the recovery area and committed to a robust \nL173: release strategy. After years of inaction, FWS released eight wolves to the wild\nL174: in North Carolina and plan to release nine more wolves in the spring of 2022. \nL175: This is an incredible win for this critically endangered species whose \nL176: population has dwindled down to single digits in the wild because of \nL177: mismanagement, vehicle strikes and poaching. DEFENDERS CONTINUED TO LEAD EFFORTS\nL178: TO PROTECT THE FLORIDA MANATEE, a beloved species that suffered the deadliest \nL179: year on record in 2021, tragically surpassing 1,000 deaths because of water \nL180: pollution and lack of warm water habitat. Defenders led advocacy and education \nL181: aimed at restoring the natural flow of the dammed Ocklawaha River, which would \nL182: provide critical warm-water habitat that manatees need to survive. Defenders’ \nL183: legal team continued to fight for manatees in the courts, holding government \nL184: agencies accountable for protecting critical habitat and addressing the \nL185: devastating water pollution that is killing the seagrass and causing manatees to\nL186: starve. DAVID TES | SAM FRENZY DRAW DEFENDERS.ORG 11 ===== Page 12 ===== In \nL187: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters \nL188: contributed more than $49 million for wildlife and wildlife habitat. This figure\nL189: includes all those who generously donated their time and expertise. The \nL190: steadfast support of our donors allows Defenders to sustain our program and \nL191: public education efforts in the field, the courts and on Capitol Hill. 2021 \nL192: SOURCES OF FUNDS Grants and contributions $29,057 Bequests, trusts and split \nL193: interests $7,692 Income from investments, annuity reserve funds and trusts \nL194: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total \nL195: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency \nL196: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total \nL197: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145 \nL198: Net Assets, End of the Year $45,144 Dollars are in thousands. 12 DEFENDERS OF \nL199: WILDLIFE Grants and contributions 58% Income from investments 7% Requests, \nL200: trusts and split interests 15% Royalties and other income 7% Contributed \nL201: services 12% Program and support services 97% Fundraising 3% ===== Page 13 =====\nL202: DEFENDERS.ORG 13 ===== Page 14 ===== John Dayton, Esq.* Chair Texas Sheryl \nL203: Sachs* Vice Chair Connecticut Whitfield Bailey* Secretary Tennessee Mark Caylor*\nL204: Treasurer District of Columbia Sylvia Baca* California Donald Barry, Esq. \nL205: Oregon Dinah Bear, Esq. Arizona Kim O’Keefe Beck, Ph.D California Gabriela \nL206: Chavarria, Ph.D Washington Jamie Rappaport Clark* President & CEO Virginia \nL207: Caroline D. Gabel Maryland Keith M. Harper, Esq. Maryland John Donovan, Esq. \nL208: Montana and California James M. Hecker, Esq. Virginia Judith Ponsikoff, Ph.D* \nL209: California Terry L. Root, Ph.D Florida BOARD EMERITUS Terry C. Pelster, Esq. New\nL210: York *Executive Committee member* ST A F F & A D V I S O R S SCIENCE ADVISORS \nL211: Gabriela Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, \nL212: Ph.D Arizona State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D\nL213: Universidad Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State \nL214: University Terry L. Root, Ph.D Stanford University Mark Schwartz, Ph.D \nL215: University of California-Davis David S. Wilcox, Ph.D Princeton University Rae ",
284
+ "tool_call_id": null
285
+ },
286
+ {
287
+ "role": "assistant",
288
+ "content": "",
289
+ "reasoning_content": "Scrolling further for \"Contributed services 12%\". Already seen. So the annual report is Defenders of Wildlife 2021 annual report.\n\nNext clue: \"The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%.\"\n\nThus, the following year (2022) annual report of Defenders of Wildlife includes mention of rescinding plan to open most of 22 million acres of NPR-A to oil and gas drilling. Also contributed services decreased to 10% (from 12% in 2021). So we need to find the 2022 annual report for Defenders of Wildlife.\n\nSearch for \"Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska\".",
290
+ "tool_calls": [
291
+ {
292
+ "id": null,
293
+ "type": "function",
294
+ "function": {
295
+ "name": "search",
296
+ "arguments": "{\"query\": \"Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska\", \"topn\": 10, \"source\": \"web\"}"
297
+ }
298
+ }
299
+ ]
300
+ },
301
+ {
302
+ "role": "tool",
303
+ "content": "[7] Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska (Search_Results/Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska)\n**viewing lines [0 - 37] of 37**\n\nL0: \nL1: URL: Search_Results/Defenders of Wildlife 2022 annual report National Petroleum \nL2: Reserve Alaska\nL3: # Search Results\nL4: \nL5: * 【0†CELEBRATING YEARS; publish_date: none†www.annualreports.com】 With less \nL6: than 340 right whales left in the wild, Defenders is fighting tirelessly to end \nL7: deadly lobster gear entanglements and vessel strikes that are driving ...\nL8: * 【1†Financials; publish_date: none†defenders.org】 We invite you to explore \nL9: the reports below to learn more about our activities and accomplishments, and \nL10: how we put your money to work for wildlife.\nL11: * 【2†Alaska Program Looks Back on 2022; publish_date: none†defenders.org】 Feb \nL12: 9, 2023 — Thanks to a lawsuit joined by Defenders, seven million acres were \nL13: returned to protection within the National Petroleum Reserve-Alaska (NPR-A), ...\nL14: * 【3†Defenders-of-Wildlife-2022-Financial-Statement. ...; publish_date: \nL15: none†defenders.org】 We have audited the accompanying consolidated financial \nL16: statements of Defenders of Wildlife and Affiliated Defenders of Wildlife Action \nL17: Fund (collectively, ...\nL18: * 【4†2022 Annual Report; publish_date: none†alaskaconservation.org】 Jun 13, \nL19: 2023 — In 2022, we focused on three landscapes: the Arctic. National Wildlife \nL20: Refuge, Bristol Bay, and the Tongass National Forest. In March 2022,.\nL21: * 【5†Assessment of ecological and cultural values within the ...; \nL22: publish_date: none†www.blm.gov】 This document was written to provide technical \nL23: information regarding the ecological importance of the National Petroleum \nL24: Reserve – Alaska (NPR-A). Several ...\nL25: * 【6†Accomplishments Report; publish_date: none†defenders.org】 National \nL26: Petroleum Reserve-Alaska. Identified for exceptional wildlife and cultural \nL27: values, including critical habitat for polar bears and other species ...\nL28: * 【7†2022 annual report; publish_date: none†dory-\nL29: plantain-s2zc.squarespace.com】 These projects are made possible through \nL30: collaborations with Defenders of. Wildlife, Cook Inletkeeper, Trustees for \nL31: Alaska,. Environmental Investigation Agency, ...\nL32: * 【8†23IMPACT REPORT; publish_date: none†www.annualreports.com】 Defenders of \nL33: Wildlife made incredible progress protecting wildlife and wild places in 2023, \nL34: helping shape a brighter future for imperiled species and vital ...\nL35: * 【9†From Leasing to Land Protections; publish_date: none†defenders-cci.org】 \nL36: Abstract. When you hear the name, National Petroleum Reserve - Alaska (NPR-A, or\nL37: Reserve), you might think of a barren land filled with oil wells and ...",
304
+ "tool_call_id": null
305
+ },
306
+ {
307
+ "role": "assistant",
308
+ "content": "",
309
+ "reasoning_content": "Open result 0.",
310
+ "tool_calls": [
311
+ {
312
+ "id": null,
313
+ "type": "function",
314
+ "function": {
315
+ "name": "open",
316
+ "arguments": "{\"cursor\": 7, \"id\": 0}"
317
+ }
318
+ }
319
+ ]
320
+ },
321
+ {
322
+ "role": "tool",
323
+ "content": "[8] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)\n**viewing lines [0 - 53] of 289**\n\nL0: \nL1: URL: https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-\nL2: wildlife_2022.pdf\nL3: ===== Page 1 ===== 2022 Impact Report C E L E B R A T I N G Y E A R S ===== Page\nL4: 2 ===== 2022 Defenders of Wildlife made important progress for imperiled \nL5: species and vital landscapes across the United States in 2022. GRAY WOLF | JIM \nL6: GUMMERAL MAY STOCK PRIOR Lawyers successfully challenged the previous \nL7: administration’s decision to delist the gray wolf and restored critical federal \nL8: protections under the Endangered Species Act. This latest triumph in court is \nL9: part of our ongoing battle to protect and restore gray wolves throughout their \nL10: historical range and shield them from persecution by extremist legislators in \nL11: Idaho, Montana and Wyoming. TWO MORE FATALIZED GRAY SWALLETS TO SEA TO SHARE \nL12: ALLIANCE Lobbyists worked around the clock to expand funding for wildlife \nL13: conservation in the FY2022 federal spending bill, which included $31 million (a \nL14: 44% increase) for the Bureau of Land Management’s Threatened and Endangered \nL15: Species Program, $2.5 million (an 81% increase) for the U.S. Department of \nL16: Agriculture Wildlife Services’ Nonlethal Initiative to prevent human-wildlife \nL17: conflicts and $21 million (a 320% increase) for North Atlantic right whale \nL18: conservation. 2 DEFENDERS OF WILDLIFE ===== Page 3 ===== **Policy Experts** \nL19: played a crucial role in securing international trade protections for 100 \nL20: species of sharks and rays, all 158 species of glass frogs and 73 species of \nL21: reptiles, including 21 species of desert horned lizards, at the Convention on \nL22: International Trade in Endangered Species (CITES) in Panama. \\--- **Field \nL23: Teams** worked tirelessly to protect and restore imperiled species across the \nL24: country. From Florida manatees and red wolves in the Southeast to belugas and \nL25: grizzly bears in Alaska, Defenders’ conservation experts were on the ground \nL26: saving species that need our help to survive and thrive. \\--- **Conservation \nL27: Innovation Experts** published more than 10 peer-reviewed studies on topics that\nL28: include the Cook Inlet beluga whale, golden-cheeked warbler, global parrot \nL29: biodiversity, the Endangered Species Act, the effects of mountaintop removal \nL30: mining on endangered species, the ecological importance of panthers and the \nL31: implementation of “30x30” – the globally recognized goal to which President \nL32: Biden committed the U.S. to conserve 30% of our imperiled lands and waters by \nL33: 2030. \\--- **DEFENDERS.ORG** ===== Page 4 ===== THANK YOU Defenders celebrated \nL34: our 75th anniversary in 2022—an exciting milestone that we attribute to our \nL35: unwavering dedication to our wildlife conservation mission. From helping to pass\nL36: the Endangered Species Act and other bedrock conservation laws to leading the \nL37: advocacy on the reintroduction of wolves to Yellowstone National Park and other \nL38: parts of the West, Defenders is proud to inspire, lead and ultimately celebrate \nL39: some of the most consequential conservation wins for wildlife and wild places. \nL40: It is wonderfully fitting that we also mark 2022 as a year when we successfully \nL41: defended, yet again, the original focal species and symbol of our organization, \nL42: the gray wolf. While the future for gray wolves and many other species remains \nL43: uncertain, we will never stop fighting to protect the imperiled wildlife that \nL44: need us and to restore vibrant and balanced ecosystems. Our mission is more \nL45: important than ever due to the escalating biodiversity and climate crises. \nL46: Defenders is determined to build on our accomplishments and seize new \nL47: opportunities to address these challenges through our multifaceted approach to \nL48: conservation. We focus on developing innovative and science-based solutions, \nL49: coordinating on-the-ground species and habitat restoration, promoting human-\nL50: wildlife coexistence, protecting and strengthening our bedrock environmental \nL51: laws, investing in impactful collaborations and partnerships, building a more \nL52: diverse and powerful conservation movement and challenging attacks on wildlife \nL53: through advocacy, policy and litigation. Defenders’ continued success is made ",
324
+ "tool_call_id": null
325
+ },
326
+ {
327
+ "role": "assistant",
328
+ "content": "",
329
+ "reasoning_content": "Search within PDF for \"National Petroleum Reserve\".",
330
+ "tool_calls": [
331
+ {
332
+ "id": null,
333
+ "type": "function",
334
+ "function": {
335
+ "name": "find",
336
+ "arguments": "{\"cursor\": 8, \"pattern\": \"Petroleum\"}"
337
+ }
338
+ }
339
+ ]
340
+ },
341
+ {
342
+ "role": "tool",
343
+ "content": "[9] Find results for text: `petroleum` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=petroleum)\n**viewing lines [0 - 4] of 4**\n\nL0: # 【0†match at L201】\nL1: the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas \nL2: drilling and removed protections for important wildlife habitat. Protections \nL3: have now been restored for nearly half of the reserve’s pristine lands, which \nL4: are vital habitat for shorebirds, denning polar bears and tens of thousands of ",
344
+ "tool_call_id": null
345
+ },
346
+ {
347
+ "role": "assistant",
348
+ "content": "",
349
+ "reasoning_content": "Open around L190.",
350
+ "tool_calls": [
351
+ {
352
+ "id": null,
353
+ "type": "function",
354
+ "function": {
355
+ "name": "open",
356
+ "arguments": "{\"cursor\": 8, \"loc\": 180}"
357
+ }
358
+ }
359
+ ]
360
+ },
361
+ {
362
+ "role": "tool",
363
+ "content": "[10] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)\n**viewing lines [180 - 227] of 289**\n\nL180: the sixth successful transfer of bison to the Assiniboine and Sioux Tribes of \nL181: Fort Peck since 2019. \\--- **SWIFT FIX KITS | © ROSIMA PAELARINTSKIMMA MADDIAL \nL182: 200 AND CONSERVATION BIOLOGY INSTITUTE** \\--- **Celebrating our third year** of \nL183: a collaborative program with the Aaniih and Nakoda Tribes and others to restore \nL184: swift foxes to the Fort Belknap Indian Reservation in Montana, Defenders helped \nL185: with the release of 28 more swift foxes. With over 100 foxes reintroduced \nL186: through this program, monitoring efforts show that they are reproducing in the \nL187: wild—a critical measure of success for a self-sustaining population. \\--- \nL188: **Defenders continued to lead the way** for conserving and recovering the \nL189: endangered black-footed ferret, supporting the black-footed ferret survey for \nL190: the Fort Belknap Indian community. Thirty-six ferrets were vaccinated against \nL191: sylvatic plague and two dozen kits were released in the wild. \\--- **10 \nL192: DEFENDERS OF WILDLIFE** ===== Page 11 ===== Defenders helped to bring hope for \nL193: recovery for the endangered military macaw, adding 11 fledglings to a growing \nL194: wild population in Puerta Vallarta, Mexico, that is under pressure from habitat \nL195: loss and poachers for the illegal pet trade. Accord- ing to our recent report, \nL196: the 2008 parrot trade ban that Defenders fought to achieve is working. \nL197: Preventing more than 30,000 parrots from being illegally trapped each year, the \nL198: trade ban has resulted in a 47% decrease in the illegal trade of parrots and an \nL199: 88% decrease in U.S. seizures of Mexican parrots. As a result of a Defenders \nL200: lawsuit, BLM rescinded the previous administration’s plan that opened most of \nL201: the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas \nL202: drilling and removed protections for important wildlife habitat. Protections \nL203: have now been restored for nearly half of the reserve’s pristine lands, which \nL204: are vital habitat for shorebirds, denning polar bears and tens of thousands of \nL205: caribou in the Teshekpuk caribou herd. Our new storymap illustrates why the \nL206: reserve is so important to wildlife. Defenders marked the 20th anniversary of \nL207: our Sea Otter Awareness Week. In celebration of this milestone, we sponsored \nL208: state legislation, signed by Gov. Gavin Newsom, that formally recognizes the \nL209: event’s anniversary and acknowledges the vital role that sea otters play in \nL210: California’s nearshore ecosystems. This annual celebration provides a unique \nL211: opportunity to raise awareness of the important role sea otters play in \nL212: maintaining ecosystem health, our need to coexist with sea otters and the \nL213: efforts of Defenders and others in advancing sea otter conservation. \nL214: DEFENDERS.ORG ===== Page 12 ===== FINANCIAL REPORT In 2022, Defenders of \nL215: Wildlife’s nearly 2.2 million members and supporters contributed more than $41 \nL216: million for wildlife and wildlife habitat. This figure includes all those who \nL217: generously donated their time and expertise. The steadfast support of our donors\nL218: allows Defenders to sustain our program and public education efforts in the \nL219: field, the courts and on Capitol Hill. 2022 SOURCES OF FUNDS Grants and \nL220: contributions $28,946 Bequests, trusts and split interests $13,928 Income from \nL221: investments, annuity reserve funds and trusts (-$7,495) Royalties and other \nL222: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS \nL223: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising \nL224: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets\nL225: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433\nL226: Dollars are in thousands. 12 DEFENDERS OF WILDLIFE Grants and contributions 59%\nL227: Contributed services 10% Royalties and other income 2% Bequests, trusts and ",
364
+ "tool_call_id": null
365
+ },
366
+ {
367
+ "role": "assistant",
368
+ "content": "",
369
+ "reasoning_content": "Scrolling a bit more for \"Contributed services 10%\". Already seen. So indeed the 2022 annual report shows contributed services 10%, down from 12% in 2021.\n\nThus the organization is Defenders of Wildlife.\n\nThe question: \"Who was the emeritus board member that year? Name only, no titles.\"\n\nWe need to identify the emeritus board member listed in the 2022 annual report. From the 2021 annual report, we saw \"BOARD EMERITUS Terry C. Pelster, Esq.\" However, that is for 2021. The question asks \"Who was the emeritus board member that year?\" That year refers to the following year (2022). So we need to check the 2022 annual report for the emeritus board member.\n\nOpen the 2022 PDF near the board list. Search for \"Emeritus\".",
370
+ "tool_calls": [
371
+ {
372
+ "id": null,
373
+ "type": "function",
374
+ "function": {
375
+ "name": "find",
376
+ "arguments": "{\"cursor\": 8, \"pattern\": \"Emeritus\"}"
377
+ }
378
+ }
379
+ ]
380
+ },
381
+ {
382
+ "role": "tool",
383
+ "content": "[11] Find results for text: `emeritus` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=emeritus)\n**viewing lines [0 - 10] of 10**\n\nL0: # 【0†match at L237】\nL1: Sisk, Ph.D British Columbia, Canada BOARD EMERITUS Terry C. Pelster, Esq. New \nL2: York *Executive Committee member* STAFF & ADVISORS SCIENCE ADVISORS Gabriela \nL3: Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, Ph.D Arizona\nL4: State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D Universidad\nL5: \nL6: # 【1†match at L243】\nL7: Davis Thomas D. Sisk, Ph.D Emeritus Professor Northern Arizona University David \nL8: S. Wilcox, Ph.D Princeton University Rae Wynn-Grant, Ph.D National Geographic \nL9: Society SENIOR STAFF Jamie Rappaport Clark President & CEO Thu Pham Chief of \nL10: Staff James Stofan Senior Vice President, Operations McCrystle Adams Vice ",
384
+ "tool_call_id": null
385
+ },
386
+ {
387
+ "role": "assistant",
388
+ "content": "",
389
+ "reasoning_content": "Open around L230.",
390
+ "tool_calls": [
391
+ {
392
+ "id": null,
393
+ "type": "function",
394
+ "function": {
395
+ "name": "open",
396
+ "arguments": "{\"cursor\": 8, \"loc\": 220}"
397
+ }
398
+ }
399
+ ]
400
+ },
401
+ {
402
+ "role": "tool",
403
+ "content": "[12] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)\n**viewing lines [220 - 267] of 289**\n\nL220: contributions $28,946 Bequests, trusts and split interests $13,928 Income from \nL221: investments, annuity reserve funds and trusts (-$7,495) Royalties and other \nL222: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS \nL223: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising \nL224: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets\nL225: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433\nL226: Dollars are in thousands. 12 DEFENDERS OF WILDLIFE Grants and contributions 59%\nL227: Contributed services 10% Royalties and other income 2% Bequests, trusts and \nL228: split interests 29% TOTAL REVENUE Income from investments -15% Program and \nL229: support services 98% USE OF FUNDS Fundraising 2% ===== Page 13 ===== \nL230: DEFENDERS.ORG 13 ===== Page 14 ===== BOARD OF DIRECTORS John Dayton, Esq.* Chair\nL231: Texas Sheryl Sachs* Vice Chair Connecticut Dinah Bear, Esq.* Secretary Arizona \nL232: Mark Caylor* Treasurer District of Columbia Susan Alexander Connecticut Sylvia \nL233: Baca* California Whitefield Bailey Tennessee Donald Barry, Esq. Oregon Kim \nL234: O’Keefe Beck, Ph.D California Gabriela Chavarria, Ph.D Washington Jamie \nL235: Rappaport Clark* President & CEO Virginia John Donovan, Esq.* Montana and \nL236: California Caroline D. Gabel Maryland Terry L. Root, Ph.D New Mexico Thomas D. \nL237: Sisk, Ph.D British Columbia, Canada BOARD EMERITUS Terry C. Pelster, Esq. New \nL238: York *Executive Committee member* STAFF & ADVISORS SCIENCE ADVISORS Gabriela \nL239: Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, Ph.D Arizona\nL240: State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D Universidad\nL241: Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State University Terry \nL242: L. Root, Ph.D Stanford University Mark Schwartz, Ph.D University of California-\nL243: Davis Thomas D. Sisk, Ph.D Emeritus Professor Northern Arizona University David \nL244: S. Wilcox, Ph.D Princeton University Rae Wynn-Grant, Ph.D National Geographic \nL245: Society SENIOR STAFF Jamie Rappaport Clark President & CEO Thu Pham Chief of \nL246: Staff James Stofan Senior Vice President, Operations McCrystle Adams Vice \nL247: President, Conservation Law Matt Braughler Vice President, Philanthropy Rachel \nL248: Brittin Vice President, Communications Shawn Cantrell Vice President, Field \nL249: Conservation Programs Robert L. Dewey Vice President, Government Relations & \nL250: External Affairs Monica Goldberg Vice President, Landscape Conservation Sharon \nL251: Morris-Smith Vice President of Human Resources Elizabeth Nichols Vice President,\nL252: Finance/CFO Anthony Pegues Vice President, Diversity, Equity, Inclusion, \nL253: Justice Lindsay Rosa, Ph.D Vice President, Conservation Research and Innovation \nL254: Brandace Swimley Vice President, Integrated Marketing 14 DEFENDERS OF WILDLIFE \nL255: ===== Page 15 ===== There are many ways to support Defenders of Wildlife and \nL256: protect our imperiled wildlife. To learn more or donate, contact us at \nL257: 1-800-385-9712, visit Defenders.org/donate or choose one of the following \nL258: options. ➤ Transfer appreciated assets directly to Defenders and maximize \nL259: potential tax benefits. Defenders.org/ donate-stocks-help-save-wildlife ➤ Become\nL260: a Leadership Council member with a single donation of $1,000 or more and enjoy \nL261: more personal involvement with Defenders. Defenders.org/leadershipcouncil ➤ If \nL262: you are 70.5 or older, roll over a portion of your IRA to benefit wildlife. \nL263: Remember, these gifts must come directly from your IRA account. \nL264: Defenders.org/planned-giving/gifts-your-retirement-plan ➤ Get started on a \nL265: contribution from your donor-advised fund Defenders.org/donor-advised-funds ➤ \nL266: Check with your personnel office or that of your spouse/partner about your \nL267: company’s Corporate Matching Gifts program or visit us at ",
404
+ "tool_call_id": null
405
+ },
406
+ {
407
+ "role": "assistant",
408
+ "content": "",
409
+ "reasoning_content": "Thus the emeritus board member is Terry C. Pelster. However, the question might ask for \"Who was the emeritus board member that year?\" Possibly referring to the year of the annual report that mentioned the rescinded plan and contributed services decrease. That is 2022. So the answer is Terry C. Pelster.\n\nBut let's verify that the 2022 annual report indeed includes the mention of contributed services decreasing to 10%. We saw that. So answer is Terry C. Pelster.\n\nBut maybe the question is tricky: \"Who was the emeritus board member that year?\" Could be the emeritus board member listed in the 2021 annual report? However, the question says \"That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%.\"\n\nThus the \"following year\" annual report is 2022. The question likely asks: \"Who was the emeritus board member that year?\" Meaning the emeritus board member listed in that annual report (2022). So answer: Terry C. Pelster.\n\nBut let's confirm that the 2021 annual report also shows emeritus board member Terry C. Pelster. Indeed we saw that. But perhaps the 2022 report might have a different emeritus board member. We saw it's the same. So answer is Terry C. Pelster.\n\nHowever, the question might be about a different organization. Let's verify the details.\n\nFirst clue: Press release about Kenya vaccine doses on Dec 5, 2021. That is likely a World Bank press release. Search for \"World Bank Kenya vaccine doses December 2021 press release\".",
410
+ "tool_calls": [
411
+ {
412
+ "id": null,
413
+ "type": "function",
414
+ "function": {
415
+ "name": "search",
416
+ "arguments": "{\"query\": \"As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines\", \"topn\": 10, \"source\": \"web\"}"
417
+ }
418
+ }
419
+ ]
420
+ },
421
+ {
422
+ "role": "tool",
423
+ "content": "[13] As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines (Search_Results/As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines)\n**viewing lines [0 - 41] of 41**\n\nL0: \nL1: URL: Search_Results/As of December 5, 2021, Kenya had received a total of \nL2: 16,201,670 vaccines\nL3: # Search Results\nL4: \nL5: * 【0†Kenya's Economy is Showing Resilience as Output Rises ...; publish_date: \nL6: none†www.worldbank.org】 Dec 14, 2021 — As of December 5, 2021, Kenya had \nL7: received a total of 16,201,670 vaccines, with 7,583,134 administered. While \nL8: vaccine acceptance is ...\nL9: * 【1†Unmet need for COVID-19 vaccination coverage in Kenya - PMC; \nL10: publish_date: none†pmc.ncbi.nlm.nih.gov】 by SK Muchiri · 2022 · Cited by 42 — As\nL11: of December 2021, six counties had a vaccination coverage of less than 5%. \nL12: These counties include Garissa, Mandera, Marsabit, Tana River, Turkana, and ...\nL13: * 【2†MINISTRY OF HEALTH; publish_date: none†covidhub.mediacouncil.or.ke】 Dec \nL14: 1, 2021 — • Total Covid-19 Vaccines Received to date- 16,201,670 ... Table 10: \nL15: Vaccine Logistics Received in the Country as at 5th, December 2021.\nL16: * 【3†COVID-19 vaccination refusal trends in Kenya over 2021 - PMC; \nL17: publish_date: none†pmc.ncbi.nlm.nih.gov】 by RT Rego · 2023 · Cited by 21 — We \nL18: assessed vaccine refusal over time in Kenya, and characterized factors \nL19: associated with changes in vaccination refusal.\nL20: * 【4†Ciheb-Kenya on the Front Lines of the COVID-19 Vaccine ...; publish_date:\nL21: none†www.ciheb.org】 Of the 721,509 persons vaccinated, 409,711 are aged 58 \nL22: years and above, 143,050 are healthcare workers, 109,593 are teachers, and \nL23: 59,155 are security officers.\nL24: * 【5†proportion of fully vaccinated adults 23.3pc.; publish_date: \nL25: none†www.facebook.com】 COVID-19: KENYA administers 150632 new vaccinations; \nL26: total jabs given now 13782963; proportion of fully vaccinated adults 23.3pc.\nL27: * 【6†National COVID-19 Vaccines Deployment and ...; publish_date: \nL28: none†www.parliament.go.ke】 Kenya has a well-established immunization program and\nL29: a robust disease surveillance and response unit (DSRU) which serves as an early\nL30: warning system, to.\nL31: * 【7†Kenya: Vaccine Requirements Violate Rights; publish_date: \nL32: none†www.hrw.org】 Dec 13, 2021 — Kenya, with an estimated adult population of \nL33: 27.2 million and a total population of 55 million, has received approximately 23\nL34: million doses as ...\nL35: * 【8†Government gives 20 million a month to get vaccinated; publish_date: \nL36: none†www.bbc.com】 Nov 22, 2021 — Although less than 10% of Kenyans are \nL37: vaccinated, the government wants to avoid a surge over Christmas.\nL38: * 【9†ministry of health kenya covid-19 immunization status ...; publish_date: \nL39: none†www.health.go.ke】 May 15, 2022 — By the end of 2021 Kenya had administered \nL40: 10.1 million doses of 5 types of Covid-19 vaccines. During this period 4.2 \nL41: million people were ...",
424
+ "tool_call_id": null
425
+ },
426
+ {
427
+ "role": "assistant",
428
+ "content": "",
429
+ "reasoning_content": "Open result 0.",
430
+ "tool_calls": [
431
+ {
432
+ "id": null,
433
+ "type": "function",
434
+ "function": {
435
+ "name": "open",
436
+ "arguments": "{\"cursor\": 13, \"id\": 0}"
437
+ }
438
+ }
439
+ ]
440
+ },
441
+ {
442
+ "role": "tool",
443
+ "content": "[14] www.worldbank.org (https://www.worldbank.org/en/news/press-release/2021/12/14/kenya-s-economy-is-showing-resilience-as-output-rises-above-pre-pandemic-levels-driven-by-a-rebound-in-the-services-sect)\n**viewing lines [0 - 52] of 61**\n\nL0: \nL1: URL: https://www.worldbank.org/en/news/press-release/2021/12/14/kenya-s-economy-\nL2: is-showing-resilience-as-output-rises-above-pre-pandemic-levels-driven-by-a-\nL3: rebound-in-the-services-sect\nL4: NAIROBI, December 14, 2021 — Kenya’s economy has demonstrated resilience to the \nL5: COVID-19 shock, with output in the first half of the year rising above pre-\nL6: pandemic levels. In 2021 as a whole, gross domestic product (GDP) is expected to\nL7: grow by 5%, one of the faster recoveries among Sub-Saharan African countries. \nL8: Overall economic performance is expected to be robust at 4.9% per year in \nL9: 2022-23, similar to the pre-pandemic pace (5% average annual growth from 2010 to\nL10: 2019). According to the 24th edition of the Kenya Economic Update, “From \nL11: Recovery to Better Jobs,” growth has been supported by rebounds in industry and,\nL12: especially, services. Agricultural output, however, fell by 0.5% year on year \nL13: in the first half of 2021 following a particularly strong performance in 2020, \nL14: partly due to below-average rains. Demand-side recovery has been supported by a \nL15: revival in private consumption, against a backdrop of improving employment \nL16: conditions and household incomes. “Kenya’s economy has shown considerable \nL17: resilience to the enormous shock of the pandemic, and this year is expected to \nL18: post one of the stronger growth rebounds in the region thanks to diversified \nL19: sources of growth and sound economic policies and management,” said Keith \nL20: Hansen, World Bank Country Director for Kenya. “However, poverty has increased, \nL21: and the buffers and coping mechanisms of households, firms, and the public \nL22: finances have been depleted.” Economic activity in Kenya has continued to adapt \nL23: to the pandemic and associated restrictions. A mix of containment measures, such\nL24: as a nightly curfew, were in effect through most of 2021, while more \nL25: economically disruptive measures such as lockdowns and travel restrictions were \nL26: phased, limiting the impact on economic activities. The vaccine rollout, which \nL27: had a slow start due to supply constraints, has picked up as new shipments of \nL28: vaccines have arrived, particularly since September. This has supported economic\nL29: recovery and growth through the third quarter of 2021. As of December 5, 2021, \nL30: Kenya had received a total of 16,201,670 vaccines, with 7,583,134 administered. \nL31: While vaccine acceptance is reportedly high there is still a long way to go \nL32: towards the government’s target of fully inoculating the adult population of \nL33: about 30 million by the end of 2022.As of December 6, 2021, about 10% of adults \nL34: (2.9 million people) had been fully vaccinated while another 16% (4.9 million \nL35: people) had received their first dose. The report also projects robust growth in\nL36: the medium-term. This outlook takes into account that some sub-sectors, such as\nL37: education, have bounced back strongly, but others such as international \nL38: tourism, have only partially and face a much more protracted recovery. The \nL39: anticipated further recovery of hotels and restaurants, trade, transport, and \nL40: other services, depends on substantial vaccination progress to help prevent new \nL41: waves of infections and associated containment measures. “Recent economic \nL42: performance has been strong and the outlook is positive, but in Kenya, as \nL43: everywhere, the future course of the pandemic remains a major source of \nL44: uncertainty, said Alex Sienaert, Senior Economist for Kenya. “To support further\nL45: recovery, reduce debt distress risks, and rebuild space for social and \nL46: development spending, it is critical that Kenya continues to implement medium-\nL47: term fiscal consolidation plans.” In addition to pandemic-related risks to the \nL48: outlook, a second key domestic risk factor stems from the drought conditions \nL49: which are affecting parts of the country and already causing severe hardship. \nL50: Should the drought intensify or spread, this would weigh on the near-term \nL51: economic outlook. Weaker global growth, higher-than-anticipated energy prices, \nL52: and tighter external financing conditions are the primary external risks. As ",
444
+ "tool_call_id": null
445
+ },
446
+ {
447
+ "role": "assistant",
448
+ "content": "",
449
+ "reasoning_content": "Thus first clue confirmed.\n\nSecond clue: Defenders of Wildlife 2021 annual report. Confirmed.\n\nThird clue: Defenders of Wildlife 2022 annual report. Confirmed.\n\nThus the answer is Terry C. Pelster.\n\nBut let's verify that the 2022 annual report indeed shows \"Contributed services 10%\". We saw that. Also confirm that 2021 report shows 12%. We saw that.\n\nThus the question: \"Who was the emeritus board member that year?\" Refers to 2022. Answer: Terry C. Pelster.\n\nHowever, the question might be asking for the emeritus board member in the 2021 report? Let's read the question carefully:\n\n\"In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles.\"\n\nThe phrase \"that year\" could refer to \"the following year\" i.e., the year of the annual report that mentioned the rescinded plan. Or could refer to \"that same year\" (the year of the press release and the first annual report). However, the phrase \"that year\" appears at the end: \"Who was the emeritus board member that year?\" It likely refers to the year of the annual report that is being discussed in the preceding sentence. The preceding sentence talks about the following year's annual report. So indeed \"that year\" is the following year (2022).\n\nThus answer: Terry C. Pelster.\n\nBut to be thorough, we should check the 2022 annual report for any other emeritus board members. The search only showed \"BOARD EMERITUS Terry C. Pelster, Esq.\" Maybe there are multiple emeritus board members. But likely only one.\n\nCheck the 2021 annual report for emeritus board member. It also shows Terry C. Pelster. So maybe the question is asking for the emeritus board member in the 2021 report. But why would they ask that? The question is about connecting the clues to identify the organization and then find the emeritus board member. The clues point to Defenders of Wildlife. The question likely asks for the emeritus board member as of the 2022 report.\n\nNevertheless, we should confirm that the 2022 report is indeed the one that mentions the rescinded plan. We saw that. Also confirm that contributed services decreased from 12% to 10%. We saw that.\n\nThus answer is Terry C. Pelster.\n\nBut let's search for \"Defenders of Wildlife emeritus board member 2022 Terry Pelster\".",
450
+ "tool_calls": [
451
+ {
452
+ "id": null,
453
+ "type": "function",
454
+ "function": {
455
+ "name": "search",
456
+ "arguments": "{\"query\": \"Terry C. Pelster Defenders of Wildlife emeritus board member\", \"topn\": 10, \"source\": \"web\"}"
457
+ }
458
+ }
459
+ ]
460
+ },
461
+ {
462
+ "role": "tool",
463
+ "content": "[15] Terry C. Pelster Defenders of Wildlife emeritus board member (Search_Results/Terry C. Pelster Defenders of Wildlife emeritus board member)\n**viewing lines [0 - 31] of 31**\n\nL0: \nL1: URL: Search_Results/Terry C. Pelster Defenders of Wildlife emeritus board member\nL2: # Search Results\nL3: \nL4: * 【0†2024 impact report; publish_date: none†defenders.org】 Terry Root, Ph.D. \nL5: New Mexico. Thomas D. Sisk, Ph.D. British Columbia, Canada. BOARD EMERITUS. \nL6: Terry C. Pelster, Esq. New York. SCIENCE ADVISORS. Leah Gerber, Ph ...\nL7: * 【1†Defenders of Wildlife; publish_date: none†www.wrongkindofgreen.org】 \nL8: Directors ; Victor M. Sher*, Chair – California ; Terry C. Pelster*, Vice Chair \nL9: – New York ; Richard Kopcho*, Treasurer – California ; Adelaide P. Gomer*, \nL10: Secretary ...\nL11: * 【2†Exhibit 12; publish_date: none†www.uschamber.com】 DECLARATION OF TERRY \nL12: PELSTER. I, Terry C. Pelster, declare as follows: 1. I am a current member of \nL13: Defenders of Wildlife (“Defenders”) and have been a member.\nL14: * 【3†2020 ANNUAL REPORT; publish_date: none†defenders.org】 Terry L. Root. \nL15: Florida. BOARD. EMERITUS. Terry C. Pelster. New York. Alan Steinberg. Florida. \nL16: SCIENCE. ADVISORS. Gabriela Chavarria,. Ph.D. Denver Museum of.\nL17: * 【4†2019 Annual Report; publish_date: none†defenders.org】 BOARD EMERITUS. \nL18: Terry C. Pelster. New York. Alan Steinberg. Florida. SCIENCE ADVISORS. Leah \nL19: Gerber, Ph.D. Arizona State University. Kathayoon Khalil, Ph.D.\nL20: * 【5†annual report; publish_date: none†defenders.org】 * Executive Committee \nL21: member. BoARD EMERITuS. Terry C. Pelster. New York. Alan Steinberg. Florida. \nL22: PRESIDENT. EMERITuS. Rodger Schlickeisen. Virginia. SCIENCE ...\nL23: * 【6†BEING THERE FOR BELUGAS; publish_date: none†wendeenicole.com】 Mar 2, 2023\nL24: — Defenders of Wildlife is a national, nonprofit member- ship ... Terry C. \nL25: Pelster, Esq. New York. *Executive Committee member outfront.\nL26: * 【7†Annual Reports; publish_date: none†www.annualreports.com】 WAYS TO GIVE \nL27: There are many ways to support Defenders of Wildlife and protect our imperiled \nL28: wildlife. ... Terry C. Pelster. New York. Alan Steinberg. Florida.\nL29: * 【8†2014 Annual Report; publish_date: none†defenders.org】 BOARD OF DIRECTORS \nL30: defenders of wildlife is a national, nonprofit ... BOARD EMERITUS. Terry C. \nL31: Pelster. New York. Alan Steinberg. Florida. Page 3. PO.",
464
+ "tool_call_id": null
465
+ },
466
+ {
467
+ "role": "assistant",
468
+ "content": "",
469
+ "reasoning_content": "Thus Terry C. Pelster is indeed the emeritus board member.\n\nNow the question might want just \"Terry C. Pelster\". Or maybe \"Terry Pelster\". Or \"Terry C. Pelster, Esq.\" but instructions say \"Name only, no titles.\" So likely \"Terry C. Pelster\". However, maybe they want \"Terry Pelster\". Better to include full name as listed: \"Terry C. Pelster\".\n\nLet's read the question again: \"Who was the emeritus board member that year? Name only, no titles.\" So we should answer just the name, no \"Esq.\" or \"Board Emeritus\". So \"Terry C. Pelster\". Or maybe \"Terry Pelster\". But the document shows \"Terry C. Pelster, Esq.\" So the name is \"Terry C. Pelster\". \n\nBetter to verify that the 2022 annual report indeed lists \"BOARD EMERITUS Terry C. Pelster, Esq.\" We saw that line.\n\nThus answer: Terry C. Pelster.\n\nBut let's double-check that the question might refer to the 2021 annual report. The phrase \"that year\" could be ambiguous. Let's parse the timeline:\n\n- Year 1: 2021. Press release in Dec 2021 about Kenya vaccine doses. Same year (2021) annual report of Defenders of Wildlife shows 12% contributed services.\n\n- Year 2: 2022. Annual report mentions rescinded plan and contributed services decreased to 10%.\n\nThe question says \"The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan ... and that contributed services had decreased by 2% to 10%.\" This is year 2.\n\nThen asks \"Who was the emeritus board member that year?\" That likely refers to year 2.\n\nThus answer: Terry C. Pelster.\n\nHowever, perhaps the emeritus board member changed between 2021 and 2022. We need to check 2022 annual report for any other emeritus board member. We saw only Terry C. Pelster. Also check 2021 annual report: same. So likely unchanged.\n\nThus answer is Terry C. Pelster.\n\nNevertheless, we should confirm that the 2022 annual report is indeed the one that includes the phrase \"BLM rescinded the previous administration’s plan that opened most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling\". We saw that in line 200-202.\n\nThus answer is Terry C. Pelster.\n\nNow we need to provide the answer in the final message. Only the name.\n\nThus final answer: Terry C. Pelster.\n\nBut let's be thorough and search for \"Defenders of Wildlife 2022 annual report contributed services 10%\".",
470
+ "tool_calls": [
471
+ {
472
+ "id": null,
473
+ "type": "function",
474
+ "function": {
475
+ "name": "find",
476
+ "arguments": "{\"cursor\": 8, \"pattern\": \"Contributed services\"}"
477
+ }
478
+ }
479
+ ]
480
+ },
481
+ {
482
+ "role": "tool",
483
+ "content": "[16] Find results for text: `contributed services` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=contributed services)\n**viewing lines [0 - 10] of 10**\n\nL0: # 【0†match at L222】\nL1: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS \nL2: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising \nL3: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets\nL4: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433\nL5: \nL6: # 【1†match at L227】\nL7: Contributed services 10% Royalties and other income 2% Bequests, trusts and \nL8: split interests 29% TOTAL REVENUE Income from investments -15% Program and \nL9: support services 98% USE OF FUNDS Fundraising 2% ===== Page 13 ===== \nL10: DEFENDERS.ORG 13 ===== Page 14 ===== BOARD OF DIRECTORS John Dayton, Esq.* Chair",
484
+ "tool_call_id": null
485
+ },
486
+ {
487
+ "role": "assistant",
488
+ "content": "",
489
+ "reasoning_content": "Thus confirmed.\n\nNow we should verify that the 2021 annual report shows contributed services 12%.",
490
+ "tool_calls": [
491
+ {
492
+ "id": null,
493
+ "type": "function",
494
+ "function": {
495
+ "name": "find",
496
+ "arguments": "{\"cursor\": 4, \"pattern\": \"Contributed services\"}"
497
+ }
498
+ }
499
+ ]
500
+ },
501
+ {
502
+ "role": "tool",
503
+ "content": "[17] Find results for text: `contributed services` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf/find?pattern=contributed services)\n**viewing lines [0 - 4] of 4**\n\nL0: # 【0†match at L194】\nL1: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total \nL2: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency \nL3: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total \nL4: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145 ",
504
+ "tool_call_id": null
505
+ },
506
+ {
507
+ "role": "assistant",
508
+ "content": "",
509
+ "reasoning_content": "Open around L190-200 to see percentage.",
510
+ "tool_calls": [
511
+ {
512
+ "id": null,
513
+ "type": "function",
514
+ "function": {
515
+ "name": "open",
516
+ "arguments": "{\"cursor\": 4, \"loc\": 185}"
517
+ }
518
+ }
519
+ ]
520
+ },
521
+ {
522
+ "role": "tool",
523
+ "content": "[18] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)\n**viewing lines [185 - 232] of 261**\n\nL185: devastating water pollution that is killing the seagrass and causing manatees to\nL186: starve. DAVID TES | SAM FRENZY DRAW DEFENDERS.ORG 11 ===== Page 12 ===== In \nL187: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters \nL188: contributed more than $49 million for wildlife and wildlife habitat. This figure\nL189: includes all those who generously donated their time and expertise. The \nL190: steadfast support of our donors allows Defenders to sustain our program and \nL191: public education efforts in the field, the courts and on Capitol Hill. 2021 \nL192: SOURCES OF FUNDS Grants and contributions $29,057 Bequests, trusts and split \nL193: interests $7,692 Income from investments, annuity reserve funds and trusts \nL194: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total \nL195: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency \nL196: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total \nL197: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145 \nL198: Net Assets, End of the Year $45,144 Dollars are in thousands. 12 DEFENDERS OF \nL199: WILDLIFE Grants and contributions 58% Income from investments 7% Requests, \nL200: trusts and split interests 15% Royalties and other income 7% Contributed \nL201: services 12% Program and support services 97% Fundraising 3% ===== Page 13 =====\nL202: DEFENDERS.ORG 13 ===== Page 14 ===== John Dayton, Esq.* Chair Texas Sheryl \nL203: Sachs* Vice Chair Connecticut Whitfield Bailey* Secretary Tennessee Mark Caylor*\nL204: Treasurer District of Columbia Sylvia Baca* California Donald Barry, Esq. \nL205: Oregon Dinah Bear, Esq. Arizona Kim O’Keefe Beck, Ph.D California Gabriela \nL206: Chavarria, Ph.D Washington Jamie Rappaport Clark* President & CEO Virginia \nL207: Caroline D. Gabel Maryland Keith M. Harper, Esq. Maryland John Donovan, Esq. \nL208: Montana and California James M. Hecker, Esq. Virginia Judith Ponsikoff, Ph.D* \nL209: California Terry L. Root, Ph.D Florida BOARD EMERITUS Terry C. Pelster, Esq. New\nL210: York *Executive Committee member* ST A F F & A D V I S O R S SCIENCE ADVISORS \nL211: Gabriela Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, \nL212: Ph.D Arizona State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D\nL213: Universidad Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State \nL214: University Terry L. Root, Ph.D Stanford University Mark Schwartz, Ph.D \nL215: University of California-Davis David S. Wilcox, Ph.D Princeton University Rae \nL216: Wynn-Grant, Ph.D National Geographic Society SENIOR STAFF Jamie Rappaport Clark \nL217: President & CEO Thu Pham Chief of Staff Renee Stone Senior Vice President, \nL218: Conservation Programs & General Counsel James Stefan Senior Vice President, \nL219: Operations Matt Braughler Vice President, Philanthropy Rachel Brittin Vice \nL220: President, Communications Shawn Cantrell Vice President, Field Conservation \nL221: Programs Robert L. Dewey Vice President, Government Relations & External Affairs\nL222: Monica Goldberg Vice President, Landscape Conservation Sharon Morris-Smith Vice\nL223: President of Human Resources Elizabeth Nichols Vice President, Finance/CFO \nL224: Anthony Pegues Vice President, Diversity, Equity, Inclusion, Justice Lindsay \nL225: Rosa, Ph.D Director, Center for Conservation Innovation Michael P. Senatore Vice\nL226: President, Conservation Law Brandace Swimley Vice President, Integrated \nL227: Marketing 14 DEFENDERS OF WILDLIFE ===== Page 15 ===== THERE ARE MANY WAYS TO \nL228: SUPPORT DEFENDERS OF WILDLIFE and protect our imperiled wildlife. To learn more \nL229: or donate, contact us at 1-800-385-9712, visit defenders.org/donate or choose \nL230: one of the following options. ➤ Transfer appreciated assets directly to \nL231: Defenders and maximize potential tax benefits. Defenders.org/donate-stocks-help-\nL232: save-wildlife ➤ Become a Leadership Council member with a single donation of ",
524
+ "tool_call_id": null
525
+ },
526
+ {
527
+ "role": "assistant",
528
+ "content": "Terry C. Pelster",
529
+ "reasoning_content": "Thus everything matches.\n\nThus answer: Terry C. Pelster.\n\nNow we need to output only the name.\n\nThus final answer: Terry C. Pelster.\n\nBut perhaps the question expects \"Terry Pelster\". However, the document includes middle initial \"C.\" It might be better to include the full name as listed: \"Terry C. Pelster\". The instruction \"Name only, no titles\" suggests we should not include \"Esq.\" or \"Board Emeritus\". But including middle initial is likely okay.\n\nTo be safe, we could output \"Terry C. Pelster\". Let's see similar puzzles: often they expect the full name as appears in the document.\n\nThus answer: Terry C. Pelster.\n\nNow we will provide final answer.",
530
+ "tool_calls": []
531
+ }
532
+ ]
533
+ }
encoding/test_output.txt ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <|begin▁of▁sentence|>You are a helpful Assistant.
2
+
3
+ ## Tools
4
+
5
+ You have access to a set of tools you can use to answer the user's question.
6
+ You can invoke functions by writing a "<|DSML|function_calls>" block like the following as part of your reply to the user:
7
+ <|DSML|function_calls>
8
+ <|DSML|invoke name="$FUNCTION_NAME">
9
+ <|DSML|parameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</|DSML|parameter>
10
+ ...
11
+ </|DSML|invoke>
12
+ <|DSML|invoke name="$FUNCTION_NAME2">
13
+ ...
14
+ </|DSML|invoke>
15
+ </|DSML|function_calls>
16
+
17
+ String and scalar parameters should be specified as is without any escaping or quotes, while lists and objects should use JSON format. The "string" attribute should be set to "true" for string type parameters and "false" for other types (numbers, booleans, arrays, objects).
18
+
19
+ If the thinking_mode is enabled, then after function results you should strongly consider outputting a thinking block. Here is an example:
20
+
21
+ <|DSML|function_calls>
22
+ ...
23
+ </|DSML|function_calls>
24
+
25
+ <function_results>
26
+ ...
27
+ </function_results>
28
+
29
+ <think>...thinking about results</think>
30
+
31
+ Here are the functions available in JSONSchema format:
32
+ <functions>
33
+ {"name": "get_datetime", "description": "Get the current date and time", "parameters": {"type": "object", "properties": {"timezone": {"type": "string", "description": "The timezone, e.g. Asia/Shanghai, UTC"}}, "required": ["timezone"]}}
34
+ {"name": "get_weather", "description": "Get the weather for a specific date and location", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "The city name, e.g. Beijing, Hangzhou"}, "date": {"type": "string", "description": "The date in YYYY-MM-DD format"}}, "required": ["location", "date"]}}
35
+ </functions>
36
+ <|User|>明天杭州和北京的天气怎么样?<|Assistant|></think>
37
+
38
+ <|DSML|function_calls>
39
+ <|DSML|invoke name="get_datetime">
40
+ <|DSML|parameter name="timezone" string="true">Asia/Shanghai</|DSML|parameter>
41
+ </|DSML|invoke>
42
+ </|DSML|function_calls><|end▁of▁sentence|>
43
+
44
+ <function_results>
45
+ <result>{"current_date": "2024-01-15", "current_time": "14:30:00", "timezone": "Asia/Shanghai"}</result>
46
+ </function_results>
47
+
48
+ </think>
49
+
50
+ <|DSML|function_calls>
51
+ <|DSML|invoke name="get_weather">
52
+ <|DSML|parameter name="location" string="true">Hangzhou</|DSML|parameter>
53
+ <|DSML|parameter name="date" string="true">2024-01-16</|DSML|parameter>
54
+ </|DSML|invoke>
55
+ <|DSML|invoke name="get_weather">
56
+ <|DSML|parameter name="location" string="true">Beijing</|DSML|parameter>
57
+ <|DSML|parameter name="date" string="true">2024-01-16</|DSML|parameter>
58
+ </|DSML|invoke>
59
+ </|DSML|function_calls><|end▁of▁sentence|>
60
+
61
+ <function_results>
62
+ <result>{"location": "Hangzhou", "date": "2024-01-16", "temperature_high": "12", "temperature_low": "5", "weather": "多云", "humidity": "65%"}</result>
63
+ <result>{"location": "Beijing", "date": "2024-01-16", "temperature_high": "-2", "temperature_low": "-8", "weather": "晴", "humidity": "30%"}</result>
64
+ </function_results>
65
+
66
+ </think>根据查询结果,明天(2024年1月16日)的天气情况如下:
67
+
68
+ **杭州**:
69
+ - 天气:多云
70
+ - 最高温度:12°C
71
+ - 最低温度:5°C
72
+ - 湿度:65%
73
+
74
+ **北京**:
75
+ - 天气:晴
76
+ - 最高温度:-2°C
77
+ - 最低温度:-8°C
78
+ - 湿度:30%
79
+
80
+ 杭州明天会比较温暖但有些多云,而北京会很冷但是晴天。建议在北京的朋友要注意保暖!<|end▁of▁sentence|><|User|>谢谢!那后天呢?<|Assistant|><think>用户现在问后天的天气,后天是2024-01-17,我可以直接查询(因为已知今天日期)🗓️</think>
81
+
82
+ <|DSML|function_calls>
83
+ <|DSML|invoke name="get_weather">
84
+ <|DSML|parameter name="location" string="true">Hangzhou</|DSML|parameter>
85
+ <|DSML|parameter name="date" string="true">2024-01-17</|DSML|parameter>
86
+ </|DSML|invoke>
87
+ <|DSML|invoke name="get_weather">
88
+ <|DSML|parameter name="location" string="true">Beijing</|DSML|parameter>
89
+ <|DSML|parameter name="date" string="true">2024-01-17</|DSML|parameter>
90
+ </|DSML|invoke>
91
+ </|DSML|function_calls><|end▁of▁sentence|>
92
+
93
+ <function_results>
94
+ <result>{"location": "Hangzhou", "date": "2024-01-17", "temperature_high": "15", "temperature_low": "8", "weather": "小雨", "humidity": "80%"}</result>
95
+ <result>{"location": "Beijing", "date": "2024-01-17", "temperature_high": "0", "temperature_low": "-6", "weather": "多云", "humidity": "45%"}</result>
96
+ </function_results>
97
+
98
+ <think>获取到后天的天气数据,整理回复给用户📝</think>后天(2024年1月17日)的天气情况:
99
+
100
+ **杭州**:
101
+ - 天气:小雨
102
+ - 最高温度:15°C
103
+ - 最低温度:8°C
104
+ - 湿度:80%
105
+
106
+ **北京**:
107
+ - 天气:多云
108
+ - 最高温度:0°C
109
+ - 最低温度:-6°C
110
+ - 湿度:45%
111
+
112
+ 杭州后天会有小雨,温度略有回升,记得带伞。北京会稍微暖和一点,但依然��冷,请继续做好保暖措施。<|end▁of▁sentence|>
encoding/test_output_search_w_date.txt ADDED
The diff for this file is too large to render. See raw diff
 
encoding/test_output_search_wo_date.txt ADDED
@@ -0,0 +1,1069 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <|begin▁of▁sentence|><|User|>
2
+
3
+ ## Tools
4
+
5
+ You have access to a set of tools you can use to answer the user's question.
6
+ You can invoke functions by writing a "<|DSML|function_calls>" block like the following as part of your reply to the user:
7
+ <|DSML|function_calls>
8
+ <|DSML|invoke name="$FUNCTION_NAME">
9
+ <|DSML|parameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</|DSML|parameter>
10
+ ...
11
+ </|DSML|invoke>
12
+ <|DSML|invoke name="$FUNCTION_NAME2">
13
+ ...
14
+ </|DSML|invoke>
15
+ </|DSML|function_calls>
16
+
17
+ String and scalar parameters should be specified as is without any escaping or quotes, while lists and objects should use JSON format. The "string" attribute should be set to "true" for string type parameters and "false" for other types (numbers, booleans, arrays, objects).
18
+
19
+ If the thinking_mode is enabled, then after function results you should strongly consider outputting a thinking block. Here is an example:
20
+
21
+ <|DSML|function_calls>
22
+ ...
23
+ </|DSML|function_calls>
24
+
25
+ <function_results>
26
+ ...
27
+ </function_results>
28
+
29
+ <think>...thinking about results</think>
30
+
31
+ Here are the functions available in JSONSchema format:
32
+ <functions>
33
+ {"name": "search", "description": "Searches for information related to query and displays topn results.", "parameters": {"type": "object", "properties": {"query": {"type": "string", "description": "The search query string"}, "topn": {"type": "integer", "description": "Number of top results to display", "default": 10}, "source": {"type": "string", "description": "Source to search within", "enum": ["web", "news"], "default": "web"}}, "required": ["query"], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#"}}
34
+ {"name": "open", "description": "Opens the link id from the page indicated by cursor starting at line number loc, showing num_lines lines. Valid link ids are displayed with the formatting: 【{id}†.*】. If cursor is not provided, the most recent page is implied. If id is a string, it is treated as a fully qualified URL associated with source. If loc is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available. Use this function without id to scroll to a new location of an opened page.", "parameters": {"type": "object", "properties": {"id": {"description": "Link ID (number) or fully qualified URL (string)", "anyOf": [{"type": "integer"}, {"type": "string"}], "default": -1}, "cursor": {"type": "integer", "description": "Page cursor indicator", "default": -1}, "loc": {"type": "integer", "description": "Starting line number", "default": -1}, "num_lines": {"type": "integer", "description": "Number of lines to show", "default": -1}, "view_source": {"type": "boolean", "description": "Whether to view source", "default": false}, "source": {"type": "string", "description": "Source associated with the URL", "enum": ["web", "news"], "default": "web"}}, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#"}}
35
+ {"name": "find", "description": "Finds exact matches of pattern in the current page, or the page given by cursor.", "parameters": {"type": "object", "properties": {"pattern": {"type": "string", "description": "The exact pattern to search for"}, "cursor": {"type": "integer", "description": "Page cursor to search in", "default": -1}}, "required": ["pattern"], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#"}}
36
+ {"name": "python", "description": "Use this tool to execute Python code. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files). When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 120.0 seconds.", "parameters": {"type": "object", "properties": {"code": {"type": "string", "description": "The python code to execute in the jupyter environment."}}, "required": ["code"], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#"}}
37
+ </functions>
38
+
39
+
40
+ # The user's message is: In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles.<|Assistant|><think>The user asks: "In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles."
41
+
42
+ We need to parse the puzzle.
43
+
44
+ We have clues about a press release about vaccine doses for a country, dated between Dec 1-15 inclusive, reporting as of 5th of that month total doses received 16,201,670 and administered 7,583,134. That is likely a COVID-19 vaccine update for some country. Which country? Might be a smaller country. Could be something like "Uganda", "Zimbabwe", "Kenya", "Philippines", etc. Let's search.
45
+
46
+ Search for "16,201,670 vaccine doses" "7,583,134 administered".</think>
47
+
48
+ <|DSML|function_calls>
49
+ <|DSML|invoke name="search">
50
+ <|DSML|parameter name="query" string="true">"16,201,670" vaccine doses</|DSML|parameter>
51
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
52
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
53
+ </|DSML|invoke>
54
+ </|DSML|function_calls><|end▁of▁sentence|>
55
+
56
+ <function_results>
57
+ <result>[0] "16,201,670" vaccine doses (Search_Results/"16,201,670" vaccine doses)
58
+ **viewing lines [0 - 38] of 38**
59
+
60
+ L0:
61
+ L1: URL: Search_Results/"16,201,670" vaccine doses
62
+ L2: # Search Results
63
+ L3:
64
+ L4: * 【0†Kenya's Economy is Showing Resilience as Output Rises ...; publish_date:
65
+ L5: none†www.worldbank.org】 Dec 14, 2021 — As of December 5, 2021, Kenya had
66
+ L6: received a total of 16,201,670 vaccines, with 7,583,134 administered. While
67
+ L7: vaccine acceptance is ...
68
+ L8: * 【1†MINISTRY OF HEALTH; publish_date: none†covidhub.mediacouncil.or.ke】 Dec
69
+ L9: 1, 2021 — Total Doses Received 16,201,670. Table 10 gives the total vaccines
70
+ L10: received since the start of Covid -19 vaccination exercise in the country.
71
+ L11: * 【2†Output Result Page; publish_date: none†open.unicef.org】 ... 16,201,670
72
+ L12: doses of multiple vaccines nationwide and full vaccination of 15.5 per cent with
73
+ L13: two doses of COVID-19 vaccine as of 31 December 2021.
74
+ L14: * 【3†rebased GDP; publish_date: none†documents1.worldbank.org】 Dec 7, 2021 —
75
+ L15: As of December 5, 2021,. Kenya had received a total of 16,201,670 vaccines,
76
+ L16: with. 7,583,134 administered. Vaccine acceptance is reportedly high.
77
+ L17: * 【4†Integrated Annual Report; publish_date: none†www.co-opbank.co.ke】 May 27,
78
+ L18: 2022 — ... doses of Covid-19 vaccines and administered close to 17 million ...
79
+ L19: 16,201,670, huku 7,583,134 zikiwa tayari zimedungwa watu. Bado kuna ...
80
+ L20: * 【5†World Bank lifts Kenya's growth prospect to 5% in 2021; publish_date:
81
+ L21: none†www.africa-press.net】 ... 16,201,670 vaccines, with 7,583,134 administered.
82
+ L22: While vaccine acceptance is reportedly high there is still a long way to go
83
+ L23: towards the government's ...
84
+ L24: * 【6†2020 Annual Report United States; publish_date: none†www.hi-us.org】
85
+ L25: including medicine, vaccines, Covid-19 prevention kits, and kits of ...
86
+ L26: 16,201,670. 9,630,584. Prepaid expenses. 56,707. 22,601. Total current assets.
87
+ L27: * 【7†HOSPITAL CENTRAL DE LA DEFENSA; publish_date:
88
+ L28: none†contrataciondelestado.es】 16201670 X. URPA-URPA. 0. HCD-000393/00. KONTRON.
89
+ L29: 7000 ... LASER SHOT LBP-1120. CNEUR-C. NEUROFISIOLOGÍA. 0 ... VICTOREEN CT HEAD
90
+ L30: DOSE PHANTON. 43650-014.
91
+ L31: * 【8†body metal concentrations: Topics by ...; publish_date:
92
+ L32: none†www.science.gov】 PMID:16201670. Design Characteristics and Tobacco Metal
93
+ L33: Concentrations in Filtered Cigars. PubMed. Caruso, Rosalie V; O'Connor, Richard
94
+ L34: J; Travers, Mark J ...
95
+ L35: * 【9†Public Offices, Personal Demands: Capability in ...; publish_date:
96
+ L36: none†dokumen.pub】 ... (16201670).9 The Institutiones Politicae consists of two
97
+ L37: books. Book one deals with the origin, nature and goal of the commonwealth on
98
+ L38: the one hand, and the ...</result>
99
+ </function_results>
100
+
101
+ <think>Result shows Kenya. So the press release is about Kenya's vaccine doses as of Dec 5, 2021. The press release likely by World Bank or something. The search result shows "World Bank lifts Kenya's growth prospect..." maybe a press release dated Dec 14, 2021. But the clue says "between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered." That matches Kenya on Dec 5, 2021. So the year is 2021.
102
+
103
+ Now the next clue: "That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services."
104
+
105
+ This sounds like an environmental or conservation organization. "Nearly 2.2 million members and supporters contributed over $49 million." That might be something like "The Nature Conservancy", "World Wildlife Fund", "Sierra Club", "Environmental Defense Fund", "Natural Resources Defense Council", etc. Let's search.
106
+
107
+ Search for "nearly 2.2 million members and supporters contributed over $49 million" "12% of total revenue coming from contributed services".</think>
108
+
109
+ <|DSML|function_calls>
110
+ <|DSML|invoke name="search">
111
+ <|DSML|parameter name="query" string="true">"nearly 2.2 million members" "contributed over $49 million"</|DSML|parameter>
112
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
113
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
114
+ </|DSML|invoke>
115
+ </|DSML|function_calls><|end▁of▁sentence|>
116
+
117
+ <function_results>
118
+ <result>[1] "nearly 2.2 million members" "contributed over $49 million" (Search_Results/"nearly 2.2 million members" "contributed over $49 million")
119
+ **viewing lines [0 - 41] of 41**
120
+
121
+ L0:
122
+ L1: URL: Search_Results/"nearly 2.2 million members" "contributed over $49 million"
123
+ L2: # Search Results
124
+ L3:
125
+ L4: * 【0†The Global Good Fund, Microsoft and Goodwill join forces ...;
126
+ L5: publish_date: none†globalgoodfund.org】 Sep 28, 2022 — ... more than 2.2 million
127
+ L6: people. They additionally contribute close to $49 billion in local, state and
128
+ L7: federal tax revenues. When COVID hit ...
129
+ L8: * 【1†Almost 22 billion American tax dollars spent to wipe out a ...;
130
+ L9: publish_date: none†www.facebook.com】 US military funding for Israel's war crimes
131
+ L10: in Lebanon and Gaza has now cost US taxpayers over $22 billion. When millions
132
+ L11: struggle to afford the ...
133
+ L12: * 【2†Corporate America has largely abandoned its post-January ...;
134
+ L13: publish_date: none†www.citizensforethics.org】 Jul 29, 2025 — Since the January 6
135
+ L14: insurrection, over 2,000 corporate and industry group PACs have given over $174
136
+ L15: million to members of the Sedition ...
137
+ L16: * 【3†Audit shows millions in questionable taxpayer spending at ...;
138
+ L17: publish_date: none†www.aol.com】 18 hours ago — ... nearly doubled from 1.3
139
+ L18: million to about 2.2 million. That is more than one in four Washington state
140
+ L19: residents receiving Medicaid, and the ...
141
+ L20: * 【4†Incarceration and Poverty in the United States - AAF; publish_date:
142
+ L21: none†www.americanactionforum.org】 Jun 30, 2020 — The United States currently
143
+ L22: incarcerates 2.2 million people, nearly half of whom are non-violent drug
144
+ L23: offenders, accused people held pre ...
145
+ L24: * 【5†How the NRA Spends Revenue (2023); publish_date: none†paddockpost.com】
146
+ L25: Apr 25, 2025 — In 2023, the NRA reported total revenue of $176 million (compared
147
+ L26: to $211 million in 2022, $227 million in 2021, $282 million in 2020, $292 ...
148
+ L27: * 【6†NCA Newsroom; publish_date: none†www.ncausa.org】 ... 2.2 million U.S.
149
+ L28: jobs—operating in every U.S. state and territory and contributing nearly $350
150
+ L29: billion to the U.S. economy every year. For media inquiries ...
151
+ L30: * 【7†The Early Childhood Educator Workforce; publish_date:
152
+ L31: none†cscce.berkeley.edu】 About 2.2 million adults are paid to care for and
153
+ L32: educate more than 9.7 million children between birth and age five in center-
154
+ L33: based and home-based settings.
155
+ L34: * 【8†Bringing Out the Worst in Congress: CRA By the Numbers ...; publish_date:
156
+ L35: none†progressivereform.org】 Jul 14, 2025 — In fact, the top ten recipients (out
157
+ L36: of 25) account for almost $7 million, showing how these contributions are
158
+ L37: targeted and strategic. Real ...
159
+ L38: * 【9†Club 100: Over 100 Americans gave more than $100000 to ...; publish_date:
160
+ L39: none†publicintegrity.org】 Jan 7, 2022 — Through Internet investments during the
161
+ L40: 1990s, he attracted 2.2 million online accounts valued at $174 million. Schwab
162
+ L41: has been a vocal ...</result>
163
+ </function_results>
164
+
165
+ <think>Not that. Try search for "2.2 million members and supporters contributed over $49 million".</think>
166
+
167
+ <|DSML|function_calls>
168
+ <|DSML|invoke name="search">
169
+ <|DSML|parameter name="query" string="true">"2.2 million members" "contributed over $49"</|DSML|parameter>
170
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
171
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
172
+ </|DSML|invoke>
173
+ </|DSML|function_calls><|end▁of▁sentence|>
174
+
175
+ <function_results>
176
+ <result>[2] "2.2 million members" "contributed over $49" (Search_Results/"2.2 million members" "contributed over $49")
177
+ **viewing lines [0 - 2] of 2**
178
+
179
+ L0:
180
+ L1: URL: Search_Results/"2.2 million members" "contributed over $49"
181
+ L2: # Search Results</result>
182
+ </function_results>
183
+
184
+ <think>Maybe it's an annual report of a non-profit. Search for "annual report 2.2 million members contributed $49 million".</think>
185
+
186
+ <|DSML|function_calls>
187
+ <|DSML|invoke name="search">
188
+ <|DSML|parameter name="query" string="true">"2.2 million members" "annual report" $49 million</|DSML|parameter>
189
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
190
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
191
+ </|DSML|invoke>
192
+ </|DSML|function_calls><|end▁of▁sentence|>
193
+
194
+ <function_results>
195
+ <result>[3] "2.2 million members" "annual report" $49 million (Search_Results/"2.2 million members" "annual report" $49 million)
196
+ **viewing lines [0 - 36] of 36**
197
+
198
+ L0:
199
+ L1: URL: Search_Results/"2.2 million members" "annual report" $49 million
200
+ L2: # Search Results
201
+ L3:
202
+ L4: * 【0†20-F; publish_date: none†www.sec.gov】 ANNUAL REPORT PURSUANT TO SECTION
203
+ L5: ... Our membership grew from 2.1 million members as at December 31, 2023 to 2.2
204
+ L6: million members as at December 31, 2024.
205
+ L7: * 【1†Oportun Reports Fourth Quarter and Full Year 2023 ...; publish_date:
206
+ L8: none†investor.oportun.com】 Mar 12, 2024 — Oportun (Nasdaq: OPRT) is a mission-
207
+ L9: driven fintech that puts its 2.2 million members' financial goals within reach.
208
+ L10: ... annual report on ...
209
+ L11: * 【2†2 0 21; publish_date: none†www.annualreports.com】 ANNUAL REPORT. 2. 0.
210
+ L12: 21. 2. 0. 21. Page 2. 2. DEFENDERS OF WILDLIFE. 2. 0. 21. 2. 0. 21 ... In 2021,
211
+ L13: Defenders of Wildlife's nearly 2.2 million members and.
212
+ L14: * 【3†Annual report and accounts 2020; publish_date: none†www.3i.com】
213
+ L15: Disclaimer. The Annual report and accounts have been prepared solely to provide
214
+ L16: information to shareholders. ... 2.2 million members. In December 2019, we sold
215
+ L17: ...
216
+ L18: * 【4†united states securities and exchange commission; publish_date:
217
+ L19: none†s21.q4cdn.com】 Dec 14, 2018 — ☒ ANNUAL REPORT ... approximately 5.7 million
218
+ L20: Members, including approximately 2.2 million Members from the acquisition of
219
+ L21: Best Doctors.
220
+ L22: * 【5†securities and exchange commission; publish_date: none†www.sec.gov】 ...
221
+ L23: Annual Report on Form 10-K filed with the SEC on March 10, 2020. The ... 2.2
222
+ L24: million members at June 30, 2020. The following table highlights the ...
223
+ L25: * 【6†2022-23 Annual Comprehensive Financial Report - CalPERS; publish_date:
224
+ L26: none†www.calpers.ca.gov】 Nov 17, 2023 — ... annual report is consistent with
225
+ L27: these accounting principles ... 2.2 million members and health benefit services
226
+ L28: to over 1.5 million ...
227
+ L29: * 【7†Medical Centers Report; publish_date:
228
+ L30: none†regents.universityofcalifornia.edu】 Oct 8, 2019 — which will allow its 2.2
229
+ L31: million members to receive advanced and ... annual report of the University of
230
+ L32: California. For the Medical ...
231
+ L33: * 【8†Working TogeTher to make HealtH care better; publish_date:
232
+ L34: none†www.unitedhealthgroup.com】 Feb 8, 2012 — ANNUAL REPORT PURSUANT TO SECTION
233
+ L35: 13 OR 15(d) OF THE ... approximately 2.2 million members enrolled in its
234
+ L36: Medicare. Advantage ...</result>
235
+ </function_results>
236
+
237
+ <think>Result 2 shows "Defenders of Wildlife". That might be the organization. Let's open.</think>
238
+
239
+ <|DSML|function_calls>
240
+ <|DSML|invoke name="open">
241
+ <|DSML|parameter name="cursor" string="false">3</|DSML|parameter>
242
+ <|DSML|parameter name="id" string="false">2</|DSML|parameter>
243
+ </|DSML|invoke>
244
+ </|DSML|function_calls><|end▁of▁sentence|>
245
+
246
+ <function_results>
247
+ <result>[4] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)
248
+ **viewing lines [0 - 53] of 261**
249
+
250
+ L0:
251
+ L1: URL: https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-
252
+ L2: wildlife_2021.pdf
253
+ L3: ===== Page 1 ===== 2021 ANNUAL REPORT ===== Page 2 ===== # DEFENDERS OF WILDLIFE
254
+ L4: made important progress for imperiled species and vital landscapes across the
255
+ L5: United States in 2021. \--- **LAWYERS** immediately **challenged** the premature
256
+ L6: and reckless decision to strip **gray wolves** of federal **Endangered Species
257
+ L7: Act (ESA)** protections. For many decades, Defenders has led the effort to
258
+ L8: protect and restore the gray wolf, and we will continue to fight the
259
+ L9: unscientific and hostile anti-wolf policies that impede conservation progress
260
+ L10: and will carry on our unrelenting battle to restore federal protections for this
261
+ L11: iconic keystone species. \--- **LOBBYISTS** worked around the clock to keep
262
+ L12: wildlife and climate priorities in the **Infrastructure Investment and Jobs
263
+ L13: Act**. We also continue fighting to keep important wildlife and habitat funding
264
+ L14: in relevant **appropriations bills**. \--- 2 DEFENDERS OF WILDLIFE ===== Page 3
265
+ L15: ===== POLICY EXPERTS pushed forward on the urgent need for a National
266
+ L16: Biodiversity Strategy (NBS), an all-of-government approach to address the
267
+ L17: unprecedented loss of wildlife and habitat we are experiencing. We have coupled
268
+ L18: this with our new campaign to expand the National Wildlife Refuge System to
269
+ L19: preserve our nation’s only lands set aside for wildlife. By defending, funding
270
+ L20: and expanding our national wildlife refuges, we will directly address
271
+ L21: biodiversity loss and climate change while promoting increased equitable access
272
+ L22: to nature. FIELD TEAMS were on the ground helping to recover imperiled species.
273
+ L23: From panthers and sea turtles in Florida to wolves, bison and black-footed
274
+ L24: ferrets in Montana, Defenders’ conservation experts were in the field saving
275
+ L25: wildlife all over the country. CONSERVATION INNOVATION EXPERTS provided
276
+ L26: comprehensive analyses to guide policy and inform conservation strategies to
277
+ L27: reach the goal of protecting 30% of our terrestrial and marine systems by 2030
278
+ L28: (“30x30”). Defenders’ Center for Conservation Innovation (CCI) produced a report
279
+ L29: which details actions we need to take to achieve 30x30 while protecting
280
+ L30: biodiversity and addressing the climate crisis. DEFENDERS.ORG ===== Page 4 =====
281
+ L31: WE HAVE ACCOMPLISHED MUCH THIS YEAR WORKING WITH AN ADMINISTRATION THAT VALUES
282
+ L32: SCIENCE AND CONSERVATION. Our many successes include the return of protections
283
+ L33: to the Tongass National Forest in Alaska and the suspension of oil and gas
284
+ L34: leasing permits for the Arctic National Wildlife Refuge. Defenders also played a
285
+ L35: lead role in successfully defending the Migratory Bird Protection Act from
286
+ L36: attack and securing critical protections for migratory birds. Throughout 2021
287
+ L37: Defenders made important progress for the wildlife and wild places we all love,
288
+ L38: yet our nation’s wildlife still face unprecedented challenges. More and more
289
+ L39: species, including manatees, Mexican gray wolves, polar bears and North Atlantic
290
+ L40: right whales, face the very real threat of extinction because of climate
291
+ L41: change, habitat loss, pollution and inadequate protections. In our work we
292
+ L42: continue to demonstrate success by following the science, building partnerships,
293
+ L43: leading with innovative solutions and focusing on collaborative and practical
294
+ L44: approaches in defense of nature. Together we can make a lasting difference for
295
+ L45: wildlife and habitats. Your support is essential to our work and we couldn’t do
296
+ L46: it without you. Thank you for being an important part of our efforts to protect
297
+ L47: imperiled wildlife across our nation. Jamie Rappaport Clark President and CEO
298
+ L48: John Dayton Board Chair OUR NEW TELEVISION SERIES, WILDLIFE NATION WITH JEFF
299
+ L49: CORWIN, premiered in October and airs every weekend on ABC stations nationwide.
300
+ L50: This groundbreaking series takes Jeff across the country to meet Defenders’
301
+ L51: experts along with our science, tribal and community partners to educate and
302
+ L52: inspire viewers to join with Defenders to protect wildlife. While highlighting
303
+ L53: some of our important conservation work and raising awareness of important </result>
304
+ </function_results>
305
+
306
+ <think>Search within PDF for "2.2 million members".</think>
307
+
308
+ <|DSML|function_calls>
309
+ <|DSML|invoke name="find">
310
+ <|DSML|parameter name="cursor" string="false">4</|DSML|parameter>
311
+ <|DSML|parameter name="pattern" string="true">2.2</|DSML|parameter>
312
+ </|DSML|invoke>
313
+ </|DSML|function_calls><|end▁of▁sentence|>
314
+
315
+ <function_results>
316
+ <result>[5] Find results for text: `2.2` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf/find?pattern=2.2)
317
+ **viewing lines [0 - 4] of 4**
318
+
319
+ L0: # 【0†match at L187】
320
+ L1: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters
321
+ L2: contributed more than $49 million for wildlife and wildlife habitat. This figure
322
+ L3: includes all those who generously donated their time and expertise. The
323
+ L4: steadfast support of our donors allows Defenders to sustain our program and </result>
324
+ </function_results>
325
+
326
+ <think>Open around L180.</think>
327
+
328
+ <|DSML|function_calls>
329
+ <|DSML|invoke name="open">
330
+ <|DSML|parameter name="cursor" string="false">4</|DSML|parameter>
331
+ <|DSML|parameter name="loc" string="false">170</|DSML|parameter>
332
+ </|DSML|invoke>
333
+ </|DSML|function_calls><|end▁of▁sentence|>
334
+
335
+ <function_results>
336
+ <result>[6] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)
337
+ **viewing lines [170 - 215] of 261**
338
+
339
+ L170: into the wild. **10 DEFENDERS OF WILDLIFE** ===== Page 11 ===== AFTER ANOTHER
340
+ L171: SUCCESSFUL DEFENDERS LAWSUIT ON BEHALF OF THE CRITICALLY ENDANGERED RED WOLF,
341
+ L172: FWS reversed its decision to limit the recovery area and committed to a robust
342
+ L173: release strategy. After years of inaction, FWS released eight wolves to the wild
343
+ L174: in North Carolina and plan to release nine more wolves in the spring of 2022.
344
+ L175: This is an incredible win for this critically endangered species whose
345
+ L176: population has dwindled down to single digits in the wild because of
346
+ L177: mismanagement, vehicle strikes and poaching. DEFENDERS CONTINUED TO LEAD EFFORTS
347
+ L178: TO PROTECT THE FLORIDA MANATEE, a beloved species that suffered the deadliest
348
+ L179: year on record in 2021, tragically surpassing 1,000 deaths because of water
349
+ L180: pollution and lack of warm water habitat. Defenders led advocacy and education
350
+ L181: aimed at restoring the natural flow of the dammed Ocklawaha River, which would
351
+ L182: provide critical warm-water habitat that manatees need to survive. Defenders’
352
+ L183: legal team continued to fight for manatees in the courts, holding government
353
+ L184: agencies accountable for protecting critical habitat and addressing the
354
+ L185: devastating water pollution that is killing the seagrass and causing manatees to
355
+ L186: starve. DAVID TES | SAM FRENZY DRAW DEFENDERS.ORG 11 ===== Page 12 ===== In
356
+ L187: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters
357
+ L188: contributed more than $49 million for wildlife and wildlife habitat. This figure
358
+ L189: includes all those who generously donated their time and expertise. The
359
+ L190: steadfast support of our donors allows Defenders to sustain our program and
360
+ L191: public education efforts in the field, the courts and on Capitol Hill. 2021
361
+ L192: SOURCES OF FUNDS Grants and contributions $29,057 Bequests, trusts and split
362
+ L193: interests $7,692 Income from investments, annuity reserve funds and trusts
363
+ L194: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total
364
+ L195: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency
365
+ L196: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total
366
+ L197: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145
367
+ L198: Net Assets, End of the Year $45,144 Dollars are in thousands. 12 DEFENDERS OF
368
+ L199: WILDLIFE Grants and contributions 58% Income from investments 7% Requests,
369
+ L200: trusts and split interests 15% Royalties and other income 7% Contributed
370
+ L201: services 12% Program and support services 97% Fundraising 3% ===== Page 13 =====
371
+ L202: DEFENDERS.ORG 13 ===== Page 14 ===== John Dayton, Esq.* Chair Texas Sheryl
372
+ L203: Sachs* Vice Chair Connecticut Whitfield Bailey* Secretary Tennessee Mark Caylor*
373
+ L204: Treasurer District of Columbia Sylvia Baca* California Donald Barry, Esq.
374
+ L205: Oregon Dinah Bear, Esq. Arizona Kim O’Keefe Beck, Ph.D California Gabriela
375
+ L206: Chavarria, Ph.D Washington Jamie Rappaport Clark* President & CEO Virginia
376
+ L207: Caroline D. Gabel Maryland Keith M. Harper, Esq. Maryland John Donovan, Esq.
377
+ L208: Montana and California James M. Hecker, Esq. Virginia Judith Ponsikoff, Ph.D*
378
+ L209: California Terry L. Root, Ph.D Florida BOARD EMERITUS Terry C. Pelster, Esq. New
379
+ L210: York *Executive Committee member* ST A F F & A D V I S O R S SCIENCE ADVISORS
380
+ L211: Gabriela Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber,
381
+ L212: Ph.D Arizona State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D
382
+ L213: Universidad Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State
383
+ L214: University Terry L. Root, Ph.D Stanford University Mark Schwartz, Ph.D
384
+ L215: University of California-Davis David S. Wilcox, Ph.D Princeton University Rae </result>
385
+ </function_results>
386
+
387
+ <think>Scrolling further for "Contributed services 12%". Already seen. So the annual report is Defenders of Wildlife 2021 annual report.
388
+
389
+ Next clue: "The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%."
390
+
391
+ Thus, the following year (2022) annual report of Defenders of Wildlife includes mention of rescinding plan to open most of 22 million acres of NPR-A to oil and gas drilling. Also contributed services decreased to 10% (from 12% in 2021). So we need to find the 2022 annual report for Defenders of Wildlife.
392
+
393
+ Search for "Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska".</think>
394
+
395
+ <|DSML|function_calls>
396
+ <|DSML|invoke name="search">
397
+ <|DSML|parameter name="query" string="true">Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska</|DSML|parameter>
398
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
399
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
400
+ </|DSML|invoke>
401
+ </|DSML|function_calls><|end▁of▁sentence|>
402
+
403
+ <function_results>
404
+ <result>[7] Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska (Search_Results/Defenders of Wildlife 2022 annual report National Petroleum Reserve Alaska)
405
+ **viewing lines [0 - 37] of 37**
406
+
407
+ L0:
408
+ L1: URL: Search_Results/Defenders of Wildlife 2022 annual report National Petroleum
409
+ L2: Reserve Alaska
410
+ L3: # Search Results
411
+ L4:
412
+ L5: * 【0†CELEBRATING YEARS; publish_date: none†www.annualreports.com】 With less
413
+ L6: than 340 right whales left in the wild, Defenders is fighting tirelessly to end
414
+ L7: deadly lobster gear entanglements and vessel strikes that are driving ...
415
+ L8: * 【1†Financials; publish_date: none†defenders.org】 We invite you to explore
416
+ L9: the reports below to learn more about our activities and accomplishments, and
417
+ L10: how we put your money to work for wildlife.
418
+ L11: * 【2†Alaska Program Looks Back on 2022; publish_date: none†defenders.org】 Feb
419
+ L12: 9, 2023 — Thanks to a lawsuit joined by Defenders, seven million acres were
420
+ L13: returned to protection within the National Petroleum Reserve-Alaska (NPR-A), ...
421
+ L14: * 【3†Defenders-of-Wildlife-2022-Financial-Statement. ...; publish_date:
422
+ L15: none†defenders.org】 We have audited the accompanying consolidated financial
423
+ L16: statements of Defenders of Wildlife and Affiliated Defenders of Wildlife Action
424
+ L17: Fund (collectively, ...
425
+ L18: * 【4†2022 Annual Report; publish_date: none†alaskaconservation.org】 Jun 13,
426
+ L19: 2023 — In 2022, we focused on three landscapes: the Arctic. National Wildlife
427
+ L20: Refuge, Bristol Bay, and the Tongass National Forest. In March 2022,.
428
+ L21: * 【5†Assessment of ecological and cultural values within the ...;
429
+ L22: publish_date: none†www.blm.gov】 This document was written to provide technical
430
+ L23: information regarding the ecological importance of the National Petroleum
431
+ L24: Reserve – Alaska (NPR-A). Several ...
432
+ L25: * 【6†Accomplishments Report; publish_date: none†defenders.org】 National
433
+ L26: Petroleum Reserve-Alaska. Identified for exceptional wildlife and cultural
434
+ L27: values, including critical habitat for polar bears and other species ...
435
+ L28: * 【7†2022 annual report; publish_date: none†dory-
436
+ L29: plantain-s2zc.squarespace.com】 These projects are made possible through
437
+ L30: collaborations with Defenders of. Wildlife, Cook Inletkeeper, Trustees for
438
+ L31: Alaska,. Environmental Investigation Agency, ...
439
+ L32: * 【8†23IMPACT REPORT; publish_date: none†www.annualreports.com】 Defenders of
440
+ L33: Wildlife made incredible progress protecting wildlife and wild places in 2023,
441
+ L34: helping shape a brighter future for imperiled species and vital ...
442
+ L35: * 【9†From Leasing to Land Protections; publish_date: none†defenders-cci.org】
443
+ L36: Abstract. When you hear the name, National Petroleum Reserve - Alaska (NPR-A, or
444
+ L37: Reserve), you might think of a barren land filled with oil wells and ...</result>
445
+ </function_results>
446
+
447
+ <think>Open result 0.</think>
448
+
449
+ <|DSML|function_calls>
450
+ <|DSML|invoke name="open">
451
+ <|DSML|parameter name="cursor" string="false">7</|DSML|parameter>
452
+ <|DSML|parameter name="id" string="false">0</|DSML|parameter>
453
+ </|DSML|invoke>
454
+ </|DSML|function_calls><|end▁of▁sentence|>
455
+
456
+ <function_results>
457
+ <result>[8] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)
458
+ **viewing lines [0 - 53] of 289**
459
+
460
+ L0:
461
+ L1: URL: https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-
462
+ L2: wildlife_2022.pdf
463
+ L3: ===== Page 1 ===== 2022 Impact Report C E L E B R A T I N G Y E A R S ===== Page
464
+ L4: 2 ===== 2022 Defenders of Wildlife made important progress for imperiled
465
+ L5: species and vital landscapes across the United States in 2022. GRAY WOLF | JIM
466
+ L6: GUMMERAL MAY STOCK PRIOR Lawyers successfully challenged the previous
467
+ L7: administration’s decision to delist the gray wolf and restored critical federal
468
+ L8: protections under the Endangered Species Act. This latest triumph in court is
469
+ L9: part of our ongoing battle to protect and restore gray wolves throughout their
470
+ L10: historical range and shield them from persecution by extremist legislators in
471
+ L11: Idaho, Montana and Wyoming. TWO MORE FATALIZED GRAY SWALLETS TO SEA TO SHARE
472
+ L12: ALLIANCE Lobbyists worked around the clock to expand funding for wildlife
473
+ L13: conservation in the FY2022 federal spending bill, which included $31 million (a
474
+ L14: 44% increase) for the Bureau of Land Management’s Threatened and Endangered
475
+ L15: Species Program, $2.5 million (an 81% increase) for the U.S. Department of
476
+ L16: Agriculture Wildlife Services’ Nonlethal Initiative to prevent human-wildlife
477
+ L17: conflicts and $21 million (a 320% increase) for North Atlantic right whale
478
+ L18: conservation. 2 DEFENDERS OF WILDLIFE ===== Page 3 ===== **Policy Experts**
479
+ L19: played a crucial role in securing international trade protections for 100
480
+ L20: species of sharks and rays, all 158 species of glass frogs and 73 species of
481
+ L21: reptiles, including 21 species of desert horned lizards, at the Convention on
482
+ L22: International Trade in Endangered Species (CITES) in Panama. \--- **Field
483
+ L23: Teams** worked tirelessly to protect and restore imperiled species across the
484
+ L24: country. From Florida manatees and red wolves in the Southeast to belugas and
485
+ L25: grizzly bears in Alaska, Defenders’ conservation experts were on the ground
486
+ L26: saving species that need our help to survive and thrive. \--- **Conservation
487
+ L27: Innovation Experts** published more than 10 peer-reviewed studies on topics that
488
+ L28: include the Cook Inlet beluga whale, golden-cheeked warbler, global parrot
489
+ L29: biodiversity, the Endangered Species Act, the effects of mountaintop removal
490
+ L30: mining on endangered species, the ecological importance of panthers and the
491
+ L31: implementation of “30x30” – the globally recognized goal to which President
492
+ L32: Biden committed the U.S. to conserve 30% of our imperiled lands and waters by
493
+ L33: 2030. \--- **DEFENDERS.ORG** ===== Page 4 ===== THANK YOU Defenders celebrated
494
+ L34: our 75th anniversary in 2022—an exciting milestone that we attribute to our
495
+ L35: unwavering dedication to our wildlife conservation mission. From helping to pass
496
+ L36: the Endangered Species Act and other bedrock conservation laws to leading the
497
+ L37: advocacy on the reintroduction of wolves to Yellowstone National Park and other
498
+ L38: parts of the West, Defenders is proud to inspire, lead and ultimately celebrate
499
+ L39: some of the most consequential conservation wins for wildlife and wild places.
500
+ L40: It is wonderfully fitting that we also mark 2022 as a year when we successfully
501
+ L41: defended, yet again, the original focal species and symbol of our organization,
502
+ L42: the gray wolf. While the future for gray wolves and many other species remains
503
+ L43: uncertain, we will never stop fighting to protect the imperiled wildlife that
504
+ L44: need us and to restore vibrant and balanced ecosystems. Our mission is more
505
+ L45: important than ever due to the escalating biodiversity and climate crises.
506
+ L46: Defenders is determined to build on our accomplishments and seize new
507
+ L47: opportunities to address these challenges through our multifaceted approach to
508
+ L48: conservation. We focus on developing innovative and science-based solutions,
509
+ L49: coordinating on-the-ground species and habitat restoration, promoting human-
510
+ L50: wildlife coexistence, protecting and strengthening our bedrock environmental
511
+ L51: laws, investing in impactful collaborations and partnerships, building a more
512
+ L52: diverse and powerful conservation movement and challenging attacks on wildlife
513
+ L53: through advocacy, policy and litigation. Defenders’ continued success is made </result>
514
+ </function_results>
515
+
516
+ <think>Search within PDF for "National Petroleum Reserve".</think>
517
+
518
+ <|DSML|function_calls>
519
+ <|DSML|invoke name="find">
520
+ <|DSML|parameter name="cursor" string="false">8</|DSML|parameter>
521
+ <|DSML|parameter name="pattern" string="true">Petroleum</|DSML|parameter>
522
+ </|DSML|invoke>
523
+ </|DSML|function_calls><|end▁of▁sentence|>
524
+
525
+ <function_results>
526
+ <result>[9] Find results for text: `petroleum` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=petroleum)
527
+ **viewing lines [0 - 4] of 4**
528
+
529
+ L0: # 【0†match at L201】
530
+ L1: the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas
531
+ L2: drilling and removed protections for important wildlife habitat. Protections
532
+ L3: have now been restored for nearly half of the reserve’s pristine lands, which
533
+ L4: are vital habitat for shorebirds, denning polar bears and tens of thousands of </result>
534
+ </function_results>
535
+
536
+ <think>Open around L190.</think>
537
+
538
+ <|DSML|function_calls>
539
+ <|DSML|invoke name="open">
540
+ <|DSML|parameter name="cursor" string="false">8</|DSML|parameter>
541
+ <|DSML|parameter name="loc" string="false">180</|DSML|parameter>
542
+ </|DSML|invoke>
543
+ </|DSML|function_calls><|end▁of▁sentence|>
544
+
545
+ <function_results>
546
+ <result>[10] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)
547
+ **viewing lines [180 - 227] of 289**
548
+
549
+ L180: the sixth successful transfer of bison to the Assiniboine and Sioux Tribes of
550
+ L181: Fort Peck since 2019. \--- **SWIFT FIX KITS | © ROSIMA PAELARINTSKIMMA MADDIAL
551
+ L182: 200 AND CONSERVATION BIOLOGY INSTITUTE** \--- **Celebrating our third year** of
552
+ L183: a collaborative program with the Aaniih and Nakoda Tribes and others to restore
553
+ L184: swift foxes to the Fort Belknap Indian Reservation in Montana, Defenders helped
554
+ L185: with the release of 28 more swift foxes. With over 100 foxes reintroduced
555
+ L186: through this program, monitoring efforts show that they are reproducing in the
556
+ L187: wild—a critical measure of success for a self-sustaining population. \---
557
+ L188: **Defenders continued to lead the way** for conserving and recovering the
558
+ L189: endangered black-footed ferret, supporting the black-footed ferret survey for
559
+ L190: the Fort Belknap Indian community. Thirty-six ferrets were vaccinated against
560
+ L191: sylvatic plague and two dozen kits were released in the wild. \--- **10
561
+ L192: DEFENDERS OF WILDLIFE** ===== Page 11 ===== Defenders helped to bring hope for
562
+ L193: recovery for the endangered military macaw, adding 11 fledglings to a growing
563
+ L194: wild population in Puerta Vallarta, Mexico, that is under pressure from habitat
564
+ L195: loss and poachers for the illegal pet trade. Accord- ing to our recent report,
565
+ L196: the 2008 parrot trade ban that Defenders fought to achieve is working.
566
+ L197: Preventing more than 30,000 parrots from being illegally trapped each year, the
567
+ L198: trade ban has resulted in a 47% decrease in the illegal trade of parrots and an
568
+ L199: 88% decrease in U.S. seizures of Mexican parrots. As a result of a Defenders
569
+ L200: lawsuit, BLM rescinded the previous administration’s plan that opened most of
570
+ L201: the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas
571
+ L202: drilling and removed protections for important wildlife habitat. Protections
572
+ L203: have now been restored for nearly half of the reserve’s pristine lands, which
573
+ L204: are vital habitat for shorebirds, denning polar bears and tens of thousands of
574
+ L205: caribou in the Teshekpuk caribou herd. Our new storymap illustrates why the
575
+ L206: reserve is so important to wildlife. Defenders marked the 20th anniversary of
576
+ L207: our Sea Otter Awareness Week. In celebration of this milestone, we sponsored
577
+ L208: state legislation, signed by Gov. Gavin Newsom, that formally recognizes the
578
+ L209: event’s anniversary and acknowledges the vital role that sea otters play in
579
+ L210: California’s nearshore ecosystems. This annual celebration provides a unique
580
+ L211: opportunity to raise awareness of the important role sea otters play in
581
+ L212: maintaining ecosystem health, our need to coexist with sea otters and the
582
+ L213: efforts of Defenders and others in advancing sea otter conservation.
583
+ L214: DEFENDERS.ORG ===== Page 12 ===== FINANCIAL REPORT In 2022, Defenders of
584
+ L215: Wildlife’s nearly 2.2 million members and supporters contributed more than $41
585
+ L216: million for wildlife and wildlife habitat. This figure includes all those who
586
+ L217: generously donated their time and expertise. The steadfast support of our donors
587
+ L218: allows Defenders to sustain our program and public education efforts in the
588
+ L219: field, the courts and on Capitol Hill. 2022 SOURCES OF FUNDS Grants and
589
+ L220: contributions $28,946 Bequests, trusts and split interests $13,928 Income from
590
+ L221: investments, annuity reserve funds and trusts (-$7,495) Royalties and other
591
+ L222: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS
592
+ L223: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising
593
+ L224: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets
594
+ L225: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433
595
+ L226: Dollars are in thousands. 12 DEFENDERS OF WILDLIFE Grants and contributions 59%
596
+ L227: Contributed services 10% Royalties and other income 2% Bequests, trusts and </result>
597
+ </function_results>
598
+
599
+ <think>Scrolling a bit more for "Contributed services 10%". Already seen. So indeed the 2022 annual report shows contributed services 10%, down from 12% in 2021.
600
+
601
+ Thus the organization is Defenders of Wildlife.
602
+
603
+ The question: "Who was the emeritus board member that year? Name only, no titles."
604
+
605
+ We need to identify the emeritus board member listed in the 2022 annual report. From the 2021 annual report, we saw "BOARD EMERITUS Terry C. Pelster, Esq." However, that is for 2021. The question asks "Who was the emeritus board member that year?" That year refers to the following year (2022). So we need to check the 2022 annual report for the emeritus board member.
606
+
607
+ Open the 2022 PDF near the board list. Search for "Emeritus".</think>
608
+
609
+ <|DSML|function_calls>
610
+ <|DSML|invoke name="find">
611
+ <|DSML|parameter name="cursor" string="false">8</|DSML|parameter>
612
+ <|DSML|parameter name="pattern" string="true">Emeritus</|DSML|parameter>
613
+ </|DSML|invoke>
614
+ </|DSML|function_calls><|end▁of▁sentence|>
615
+
616
+ <function_results>
617
+ <result>[11] Find results for text: `emeritus` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=emeritus)
618
+ **viewing lines [0 - 10] of 10**
619
+
620
+ L0: # 【0†match at L237】
621
+ L1: Sisk, Ph.D British Columbia, Canada BOARD EMERITUS Terry C. Pelster, Esq. New
622
+ L2: York *Executive Committee member* STAFF & ADVISORS SCIENCE ADVISORS Gabriela
623
+ L3: Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, Ph.D Arizona
624
+ L4: State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D Universidad
625
+ L5:
626
+ L6: # 【1†match at L243】
627
+ L7: Davis Thomas D. Sisk, Ph.D Emeritus Professor Northern Arizona University David
628
+ L8: S. Wilcox, Ph.D Princeton University Rae Wynn-Grant, Ph.D National Geographic
629
+ L9: Society SENIOR STAFF Jamie Rappaport Clark President & CEO Thu Pham Chief of
630
+ L10: Staff James Stofan Senior Vice President, Operations McCrystle Adams Vice </result>
631
+ </function_results>
632
+
633
+ <think>Open around L230.</think>
634
+
635
+ <|DSML|function_calls>
636
+ <|DSML|invoke name="open">
637
+ <|DSML|parameter name="cursor" string="false">8</|DSML|parameter>
638
+ <|DSML|parameter name="loc" string="false">220</|DSML|parameter>
639
+ </|DSML|invoke>
640
+ </|DSML|function_calls><|end▁of▁sentence|>
641
+
642
+ <function_results>
643
+ <result>[12] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf)
644
+ **viewing lines [220 - 267] of 289**
645
+
646
+ L220: contributions $28,946 Bequests, trusts and split interests $13,928 Income from
647
+ L221: investments, annuity reserve funds and trusts (-$7,495) Royalties and other
648
+ L222: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS
649
+ L223: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising
650
+ L224: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets
651
+ L225: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433
652
+ L226: Dollars are in thousands. 12 DEFENDERS OF WILDLIFE Grants and contributions 59%
653
+ L227: Contributed services 10% Royalties and other income 2% Bequests, trusts and
654
+ L228: split interests 29% TOTAL REVENUE Income from investments -15% Program and
655
+ L229: support services 98% USE OF FUNDS Fundraising 2% ===== Page 13 =====
656
+ L230: DEFENDERS.ORG 13 ===== Page 14 ===== BOARD OF DIRECTORS John Dayton, Esq.* Chair
657
+ L231: Texas Sheryl Sachs* Vice Chair Connecticut Dinah Bear, Esq.* Secretary Arizona
658
+ L232: Mark Caylor* Treasurer District of Columbia Susan Alexander Connecticut Sylvia
659
+ L233: Baca* California Whitefield Bailey Tennessee Donald Barry, Esq. Oregon Kim
660
+ L234: O’Keefe Beck, Ph.D California Gabriela Chavarria, Ph.D Washington Jamie
661
+ L235: Rappaport Clark* President & CEO Virginia John Donovan, Esq.* Montana and
662
+ L236: California Caroline D. Gabel Maryland Terry L. Root, Ph.D New Mexico Thomas D.
663
+ L237: Sisk, Ph.D British Columbia, Canada BOARD EMERITUS Terry C. Pelster, Esq. New
664
+ L238: York *Executive Committee member* STAFF & ADVISORS SCIENCE ADVISORS Gabriela
665
+ L239: Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber, Ph.D Arizona
666
+ L240: State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D Universidad
667
+ L241: Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State University Terry
668
+ L242: L. Root, Ph.D Stanford University Mark Schwartz, Ph.D University of California-
669
+ L243: Davis Thomas D. Sisk, Ph.D Emeritus Professor Northern Arizona University David
670
+ L244: S. Wilcox, Ph.D Princeton University Rae Wynn-Grant, Ph.D National Geographic
671
+ L245: Society SENIOR STAFF Jamie Rappaport Clark President & CEO Thu Pham Chief of
672
+ L246: Staff James Stofan Senior Vice President, Operations McCrystle Adams Vice
673
+ L247: President, Conservation Law Matt Braughler Vice President, Philanthropy Rachel
674
+ L248: Brittin Vice President, Communications Shawn Cantrell Vice President, Field
675
+ L249: Conservation Programs Robert L. Dewey Vice President, Government Relations &
676
+ L250: External Affairs Monica Goldberg Vice President, Landscape Conservation Sharon
677
+ L251: Morris-Smith Vice President of Human Resources Elizabeth Nichols Vice President,
678
+ L252: Finance/CFO Anthony Pegues Vice President, Diversity, Equity, Inclusion,
679
+ L253: Justice Lindsay Rosa, Ph.D Vice President, Conservation Research and Innovation
680
+ L254: Brandace Swimley Vice President, Integrated Marketing 14 DEFENDERS OF WILDLIFE
681
+ L255: ===== Page 15 ===== There are many ways to support Defenders of Wildlife and
682
+ L256: protect our imperiled wildlife. To learn more or donate, contact us at
683
+ L257: 1-800-385-9712, visit Defenders.org/donate or choose one of the following
684
+ L258: options. ➤ Transfer appreciated assets directly to Defenders and maximize
685
+ L259: potential tax benefits. Defenders.org/ donate-stocks-help-save-wildlife ➤ Become
686
+ L260: a Leadership Council member with a single donation of $1,000 or more and enjoy
687
+ L261: more personal involvement with Defenders. Defenders.org/leadershipcouncil ➤ If
688
+ L262: you are 70.5 or older, roll over a portion of your IRA to benefit wildlife.
689
+ L263: Remember, these gifts must come directly from your IRA account.
690
+ L264: Defenders.org/planned-giving/gifts-your-retirement-plan ➤ Get started on a
691
+ L265: contribution from your donor-advised fund Defenders.org/donor-advised-funds ➤
692
+ L266: Check with your personnel office or that of your spouse/partner about your
693
+ L267: company’s Corporate Matching Gifts program or visit us at </result>
694
+ </function_results>
695
+
696
+ <think>Thus the emeritus board member is Terry C. Pelster. However, the question might ask for "Who was the emeritus board member that year?" Possibly referring to the year of the annual report that mentioned the rescinded plan and contributed services decrease. That is 2022. So the answer is Terry C. Pelster.
697
+
698
+ But let's verify that the 2022 annual report indeed includes the mention of contributed services decreasing to 10%. We saw that. So answer is Terry C. Pelster.
699
+
700
+ But maybe the question is tricky: "Who was the emeritus board member that year?" Could be the emeritus board member listed in the 2021 annual report? However, the question says "That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%."
701
+
702
+ Thus the "following year" annual report is 2022. The question likely asks: "Who was the emeritus board member that year?" Meaning the emeritus board member listed in that annual report (2022). So answer: Terry C. Pelster.
703
+
704
+ But let's confirm that the 2021 annual report also shows emeritus board member Terry C. Pelster. Indeed we saw that. But perhaps the 2022 report might have a different emeritus board member. We saw it's the same. So answer is Terry C. Pelster.
705
+
706
+ However, the question might be about a different organization. Let's verify the details.
707
+
708
+ First clue: Press release about Kenya vaccine doses on Dec 5, 2021. That is likely a World Bank press release. Search for "World Bank Kenya vaccine doses December 2021 press release".</think>
709
+
710
+ <|DSML|function_calls>
711
+ <|DSML|invoke name="search">
712
+ <|DSML|parameter name="query" string="true">As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines</|DSML|parameter>
713
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
714
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
715
+ </|DSML|invoke>
716
+ </|DSML|function_calls><|end▁of▁sentence|>
717
+
718
+ <function_results>
719
+ <result>[13] As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines (Search_Results/As of December 5, 2021, Kenya had received a total of 16,201,670 vaccines)
720
+ **viewing lines [0 - 41] of 41**
721
+
722
+ L0:
723
+ L1: URL: Search_Results/As of December 5, 2021, Kenya had received a total of
724
+ L2: 16,201,670 vaccines
725
+ L3: # Search Results
726
+ L4:
727
+ L5: * 【0†Kenya's Economy is Showing Resilience as Output Rises ...; publish_date:
728
+ L6: none†www.worldbank.org】 Dec 14, 2021 — As of December 5, 2021, Kenya had
729
+ L7: received a total of 16,201,670 vaccines, with 7,583,134 administered. While
730
+ L8: vaccine acceptance is ...
731
+ L9: * 【1†Unmet need for COVID-19 vaccination coverage in Kenya - PMC;
732
+ L10: publish_date: none†pmc.ncbi.nlm.nih.gov】 by SK Muchiri · 2022 · Cited by 42 — As
733
+ L11: of December 2021, six counties had a vaccination coverage of less than 5%.
734
+ L12: These counties include Garissa, Mandera, Marsabit, Tana River, Turkana, and ...
735
+ L13: * 【2†MINISTRY OF HEALTH; publish_date: none†covidhub.mediacouncil.or.ke】 Dec
736
+ L14: 1, 2021 — • Total Covid-19 Vaccines Received to date- 16,201,670 ... Table 10:
737
+ L15: Vaccine Logistics Received in the Country as at 5th, December 2021.
738
+ L16: * 【3†COVID-19 vaccination refusal trends in Kenya over 2021 - PMC;
739
+ L17: publish_date: none†pmc.ncbi.nlm.nih.gov】 by RT Rego · 2023 · Cited by 21 — We
740
+ L18: assessed vaccine refusal over time in Kenya, and characterized factors
741
+ L19: associated with changes in vaccination refusal.
742
+ L20: * 【4†Ciheb-Kenya on the Front Lines of the COVID-19 Vaccine ...; publish_date:
743
+ L21: none†www.ciheb.org】 Of the 721,509 persons vaccinated, 409,711 are aged 58
744
+ L22: years and above, 143,050 are healthcare workers, 109,593 are teachers, and
745
+ L23: 59,155 are security officers.
746
+ L24: * 【5†proportion of fully vaccinated adults 23.3pc.; publish_date:
747
+ L25: none†www.facebook.com】 COVID-19: KENYA administers 150632 new vaccinations;
748
+ L26: total jabs given now 13782963; proportion of fully vaccinated adults 23.3pc.
749
+ L27: * 【6†National COVID-19 Vaccines Deployment and ...; publish_date:
750
+ L28: none†www.parliament.go.ke】 Kenya has a well-established immunization program and
751
+ L29: a robust disease surveillance and response unit (DSRU) which serves as an early
752
+ L30: warning system, to.
753
+ L31: * 【7†Kenya: Vaccine Requirements Violate Rights; publish_date:
754
+ L32: none†www.hrw.org】 Dec 13, 2021 — Kenya, with an estimated adult population of
755
+ L33: 27.2 million and a total population of 55 million, has received approximately 23
756
+ L34: million doses as ...
757
+ L35: * 【8†Government gives 20 million a month to get vaccinated; publish_date:
758
+ L36: none†www.bbc.com】 Nov 22, 2021 — Although less than 10% of Kenyans are
759
+ L37: vaccinated, the government wants to avoid a surge over Christmas.
760
+ L38: * 【9†ministry of health kenya covid-19 immunization status ...; publish_date:
761
+ L39: none†www.health.go.ke】 May 15, 2022 — By the end of 2021 Kenya had administered
762
+ L40: 10.1 million doses of 5 types of Covid-19 vaccines. During this period 4.2
763
+ L41: million people were ...</result>
764
+ </function_results>
765
+
766
+ <think>Open result 0.</think>
767
+
768
+ <|DSML|function_calls>
769
+ <|DSML|invoke name="open">
770
+ <|DSML|parameter name="cursor" string="false">13</|DSML|parameter>
771
+ <|DSML|parameter name="id" string="false">0</|DSML|parameter>
772
+ </|DSML|invoke>
773
+ </|DSML|function_calls><|end▁of▁sentence|>
774
+
775
+ <function_results>
776
+ <result>[14] www.worldbank.org (https://www.worldbank.org/en/news/press-release/2021/12/14/kenya-s-economy-is-showing-resilience-as-output-rises-above-pre-pandemic-levels-driven-by-a-rebound-in-the-services-sect)
777
+ **viewing lines [0 - 52] of 61**
778
+
779
+ L0:
780
+ L1: URL: https://www.worldbank.org/en/news/press-release/2021/12/14/kenya-s-economy-
781
+ L2: is-showing-resilience-as-output-rises-above-pre-pandemic-levels-driven-by-a-
782
+ L3: rebound-in-the-services-sect
783
+ L4: NAIROBI, December 14, 2021 — Kenya’s economy has demonstrated resilience to the
784
+ L5: COVID-19 shock, with output in the first half of the year rising above pre-
785
+ L6: pandemic levels. In 2021 as a whole, gross domestic product (GDP) is expected to
786
+ L7: grow by 5%, one of the faster recoveries among Sub-Saharan African countries.
787
+ L8: Overall economic performance is expected to be robust at 4.9% per year in
788
+ L9: 2022-23, similar to the pre-pandemic pace (5% average annual growth from 2010 to
789
+ L10: 2019). According to the 24th edition of the Kenya Economic Update, “From
790
+ L11: Recovery to Better Jobs,” growth has been supported by rebounds in industry and,
791
+ L12: especially, services. Agricultural output, however, fell by 0.5% year on year
792
+ L13: in the first half of 2021 following a particularly strong performance in 2020,
793
+ L14: partly due to below-average rains. Demand-side recovery has been supported by a
794
+ L15: revival in private consumption, against a backdrop of improving employment
795
+ L16: conditions and household incomes. “Kenya’s economy has shown considerable
796
+ L17: resilience to the enormous shock of the pandemic, and this year is expected to
797
+ L18: post one of the stronger growth rebounds in the region thanks to diversified
798
+ L19: sources of growth and sound economic policies and management,” said Keith
799
+ L20: Hansen, World Bank Country Director for Kenya. “However, poverty has increased,
800
+ L21: and the buffers and coping mechanisms of households, firms, and the public
801
+ L22: finances have been depleted.” Economic activity in Kenya has continued to adapt
802
+ L23: to the pandemic and associated restrictions. A mix of containment measures, such
803
+ L24: as a nightly curfew, were in effect through most of 2021, while more
804
+ L25: economically disruptive measures such as lockdowns and travel restrictions were
805
+ L26: phased, limiting the impact on economic activities. The vaccine rollout, which
806
+ L27: had a slow start due to supply constraints, has picked up as new shipments of
807
+ L28: vaccines have arrived, particularly since September. This has supported economic
808
+ L29: recovery and growth through the third quarter of 2021. As of December 5, 2021,
809
+ L30: Kenya had received a total of 16,201,670 vaccines, with 7,583,134 administered.
810
+ L31: While vaccine acceptance is reportedly high there is still a long way to go
811
+ L32: towards the government’s target of fully inoculating the adult population of
812
+ L33: about 30 million by the end of 2022.As of December 6, 2021, about 10% of adults
813
+ L34: (2.9 million people) had been fully vaccinated while another 16% (4.9 million
814
+ L35: people) had received their first dose. The report also projects robust growth in
815
+ L36: the medium-term. This outlook takes into account that some sub-sectors, such as
816
+ L37: education, have bounced back strongly, but others such as international
817
+ L38: tourism, have only partially and face a much more protracted recovery. The
818
+ L39: anticipated further recovery of hotels and restaurants, trade, transport, and
819
+ L40: other services, depends on substantial vaccination progress to help prevent new
820
+ L41: waves of infections and associated containment measures. “Recent economic
821
+ L42: performance has been strong and the outlook is positive, but in Kenya, as
822
+ L43: everywhere, the future course of the pandemic remains a major source of
823
+ L44: uncertainty, said Alex Sienaert, Senior Economist for Kenya. “To support further
824
+ L45: recovery, reduce debt distress risks, and rebuild space for social and
825
+ L46: development spending, it is critical that Kenya continues to implement medium-
826
+ L47: term fiscal consolidation plans.” In addition to pandemic-related risks to the
827
+ L48: outlook, a second key domestic risk factor stems from the drought conditions
828
+ L49: which are affecting parts of the country and already causing severe hardship.
829
+ L50: Should the drought intensify or spread, this would weigh on the near-term
830
+ L51: economic outlook. Weaker global growth, higher-than-anticipated energy prices,
831
+ L52: and tighter external financing conditions are the primary external risks. As </result>
832
+ </function_results>
833
+
834
+ <think>Thus first clue confirmed.
835
+
836
+ Second clue: Defenders of Wildlife 2021 annual report. Confirmed.
837
+
838
+ Third clue: Defenders of Wildlife 2022 annual report. Confirmed.
839
+
840
+ Thus the answer is Terry C. Pelster.
841
+
842
+ But let's verify that the 2022 annual report indeed shows "Contributed services 10%". We saw that. Also confirm that 2021 report shows 12%. We saw that.
843
+
844
+ Thus the question: "Who was the emeritus board member that year?" Refers to 2022. Answer: Terry C. Pelster.
845
+
846
+ However, the question might be asking for the emeritus board member in the 2021 report? Let's read the question carefully:
847
+
848
+ "In a year before 2023, between December 1 and 15 (inclusive), a press release reported that as of the 5th of that month, a country had received a total of 16,201,670 vaccine doses, with 7,583,134 administered. That same year, a company's annual report revealed that its nearly 2.2 million members and supporters contributed over $49 million, with 12% of total revenue coming from contributed services. The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan to open most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling and that contributed services had decreased by 2% to 10%. Who was the emeritus board member that year? Name only, no titles."
849
+
850
+ The phrase "that year" could refer to "the following year" i.e., the year of the annual report that mentioned the rescinded plan. Or could refer to "that same year" (the year of the press release and the first annual report). However, the phrase "that year" appears at the end: "Who was the emeritus board member that year?" It likely refers to the year of the annual report that is being discussed in the preceding sentence. The preceding sentence talks about the following year's annual report. So indeed "that year" is the following year (2022).
851
+
852
+ Thus answer: Terry C. Pelster.
853
+
854
+ But to be thorough, we should check the 2022 annual report for any other emeritus board members. The search only showed "BOARD EMERITUS Terry C. Pelster, Esq." Maybe there are multiple emeritus board members. But likely only one.
855
+
856
+ Check the 2021 annual report for emeritus board member. It also shows Terry C. Pelster. So maybe the question is asking for the emeritus board member in the 2021 report. But why would they ask that? The question is about connecting the clues to identify the organization and then find the emeritus board member. The clues point to Defenders of Wildlife. The question likely asks for the emeritus board member as of the 2022 report.
857
+
858
+ Nevertheless, we should confirm that the 2022 report is indeed the one that mentions the rescinded plan. We saw that. Also confirm that contributed services decreased from 12% to 10%. We saw that.
859
+
860
+ Thus answer is Terry C. Pelster.
861
+
862
+ But let's search for "Defenders of Wildlife emeritus board member 2022 Terry Pelster".</think>
863
+
864
+ <|DSML|function_calls>
865
+ <|DSML|invoke name="search">
866
+ <|DSML|parameter name="query" string="true">Terry C. Pelster Defenders of Wildlife emeritus board member</|DSML|parameter>
867
+ <|DSML|parameter name="topn" string="false">10</|DSML|parameter>
868
+ <|DSML|parameter name="source" string="true">web</|DSML|parameter>
869
+ </|DSML|invoke>
870
+ </|DSML|function_calls><|end▁of▁sentence|>
871
+
872
+ <function_results>
873
+ <result>[15] Terry C. Pelster Defenders of Wildlife emeritus board member (Search_Results/Terry C. Pelster Defenders of Wildlife emeritus board member)
874
+ **viewing lines [0 - 31] of 31**
875
+
876
+ L0:
877
+ L1: URL: Search_Results/Terry C. Pelster Defenders of Wildlife emeritus board member
878
+ L2: # Search Results
879
+ L3:
880
+ L4: * 【0†2024 impact report; publish_date: none†defenders.org】 Terry Root, Ph.D.
881
+ L5: New Mexico. Thomas D. Sisk, Ph.D. British Columbia, Canada. BOARD EMERITUS.
882
+ L6: Terry C. Pelster, Esq. New York. SCIENCE ADVISORS. Leah Gerber, Ph ...
883
+ L7: * 【1†Defenders of Wildlife; publish_date: none†www.wrongkindofgreen.org】
884
+ L8: Directors ; Victor M. Sher*, Chair – California ; Terry C. Pelster*, Vice Chair
885
+ L9: – New York ; Richard Kopcho*, Treasurer – California ; Adelaide P. Gomer*,
886
+ L10: Secretary ...
887
+ L11: * 【2†Exhibit 12; publish_date: none†www.uschamber.com】 DECLARATION OF TERRY
888
+ L12: PELSTER. I, Terry C. Pelster, declare as follows: 1. I am a current member of
889
+ L13: Defenders of Wildlife (“Defenders”) and have been a member.
890
+ L14: * 【3†2020 ANNUAL REPORT; publish_date: none†defenders.org】 Terry L. Root.
891
+ L15: Florida. BOARD. EMERITUS. Terry C. Pelster. New York. Alan Steinberg. Florida.
892
+ L16: SCIENCE. ADVISORS. Gabriela Chavarria,. Ph.D. Denver Museum of.
893
+ L17: * 【4†2019 Annual Report; publish_date: none†defenders.org】 BOARD EMERITUS.
894
+ L18: Terry C. Pelster. New York. Alan Steinberg. Florida. SCIENCE ADVISORS. Leah
895
+ L19: Gerber, Ph.D. Arizona State University. Kathayoon Khalil, Ph.D.
896
+ L20: * 【5†annual report; publish_date: none†defenders.org】 * Executive Committee
897
+ L21: member. BoARD EMERITuS. Terry C. Pelster. New York. Alan Steinberg. Florida.
898
+ L22: PRESIDENT. EMERITuS. Rodger Schlickeisen. Virginia. SCIENCE ...
899
+ L23: * 【6†BEING THERE FOR BELUGAS; publish_date: none†wendeenicole.com】 Mar 2, 2023
900
+ L24: — Defenders of Wildlife is a national, nonprofit member- ship ... Terry C.
901
+ L25: Pelster, Esq. New York. *Executive Committee member outfront.
902
+ L26: * 【7†Annual Reports; publish_date: none†www.annualreports.com】 WAYS TO GIVE
903
+ L27: There are many ways to support Defenders of Wildlife and protect our imperiled
904
+ L28: wildlife. ... Terry C. Pelster. New York. Alan Steinberg. Florida.
905
+ L29: * 【8†2014 Annual Report; publish_date: none†defenders.org】 BOARD OF DIRECTORS
906
+ L30: defenders of wildlife is a national, nonprofit ... BOARD EMERITUS. Terry C.
907
+ L31: Pelster. New York. Alan Steinberg. Florida. Page 3. PO.</result>
908
+ </function_results>
909
+
910
+ <think>Thus Terry C. Pelster is indeed the emeritus board member.
911
+
912
+ Now the question might want just "Terry C. Pelster". Or maybe "Terry Pelster". Or "Terry C. Pelster, Esq." but instructions say "Name only, no titles." So likely "Terry C. Pelster". However, maybe they want "Terry Pelster". Better to include full name as listed: "Terry C. Pelster".
913
+
914
+ Let's read the question again: "Who was the emeritus board member that year? Name only, no titles." So we should answer just the name, no "Esq." or "Board Emeritus". So "Terry C. Pelster". Or maybe "Terry Pelster". But the document shows "Terry C. Pelster, Esq." So the name is "Terry C. Pelster".
915
+
916
+ Better to verify that the 2022 annual report indeed lists "BOARD EMERITUS Terry C. Pelster, Esq." We saw that line.
917
+
918
+ Thus answer: Terry C. Pelster.
919
+
920
+ But let's double-check that the question might refer to the 2021 annual report. The phrase "that year" could be ambiguous. Let's parse the timeline:
921
+
922
+ - Year 1: 2021. Press release in Dec 2021 about Kenya vaccine doses. Same year (2021) annual report of Defenders of Wildlife shows 12% contributed services.
923
+
924
+ - Year 2: 2022. Annual report mentions rescinded plan and contributed services decreased to 10%.
925
+
926
+ The question says "The following year, the annual report stated that the organization had played a role in rescinding a previous administration’s plan ... and that contributed services had decreased by 2% to 10%." This is year 2.
927
+
928
+ Then asks "Who was the emeritus board member that year?" That likely refers to year 2.
929
+
930
+ Thus answer: Terry C. Pelster.
931
+
932
+ However, perhaps the emeritus board member changed between 2021 and 2022. We need to check 2022 annual report for any other emeritus board member. We saw only Terry C. Pelster. Also check 2021 annual report: same. So likely unchanged.
933
+
934
+ Thus answer is Terry C. Pelster.
935
+
936
+ Nevertheless, we should confirm that the 2022 annual report is indeed the one that includes the phrase "BLM rescinded the previous administration’s plan that opened most of the 22 million acres of the National Petroleum Reserve in Alaska to oil and gas drilling". We saw that in line 200-202.
937
+
938
+ Thus answer is Terry C. Pelster.
939
+
940
+ Now we need to provide the answer in the final message. Only the name.
941
+
942
+ Thus final answer: Terry C. Pelster.
943
+
944
+ But let's be thorough and search for "Defenders of Wildlife 2022 annual report contributed services 10%".</think>
945
+
946
+ <|DSML|function_calls>
947
+ <|DSML|invoke name="find">
948
+ <|DSML|parameter name="cursor" string="false">8</|DSML|parameter>
949
+ <|DSML|parameter name="pattern" string="true">Contributed services</|DSML|parameter>
950
+ </|DSML|invoke>
951
+ </|DSML|function_calls><|end▁of▁sentence|>
952
+
953
+ <function_results>
954
+ <result>[16] Find results for text: `contributed services` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2022.pdf/find?pattern=contributed services)
955
+ **viewing lines [0 - 10] of 10**
956
+
957
+ L0: # 【0†match at L222】
958
+ L1: income $744 Contributed services $5,047 Total Revenue $41,170 USES OF FUNDS
959
+ L2: Biodiversity conservation $18,970 Constituency mobilization $16,528 Fundraising
960
+ L3: $1,029 Management and general $6,354 Total Expenses $42,881 Change in net assets
961
+ L4: (-$1,711) Net assets, start of year $45,144 Net Assets, End of the Year $43,433
962
+ L5:
963
+ L6: # 【1†match at L227】
964
+ L7: Contributed services 10% Royalties and other income 2% Bequests, trusts and
965
+ L8: split interests 29% TOTAL REVENUE Income from investments -15% Program and
966
+ L9: support services 98% USE OF FUNDS Fundraising 2% ===== Page 13 =====
967
+ L10: DEFENDERS.ORG 13 ===== Page 14 ===== BOARD OF DIRECTORS John Dayton, Esq.* Chair</result>
968
+ </function_results>
969
+
970
+ <think>Thus confirmed.
971
+
972
+ Now we should verify that the 2021 annual report shows contributed services 12%.</think>
973
+
974
+ <|DSML|function_calls>
975
+ <|DSML|invoke name="find">
976
+ <|DSML|parameter name="cursor" string="false">4</|DSML|parameter>
977
+ <|DSML|parameter name="pattern" string="true">Contributed services</|DSML|parameter>
978
+ </|DSML|invoke>
979
+ </|DSML|function_calls><|end▁of▁sentence|>
980
+
981
+ <function_results>
982
+ <result>[17] Find results for text: `contributed services` in `www.annualreports.com` (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf/find?pattern=contributed services)
983
+ **viewing lines [0 - 4] of 4**
984
+
985
+ L0: # 【0†match at L194】
986
+ L1: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total
987
+ L2: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency
988
+ L3: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total
989
+ L4: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145 </result>
990
+ </function_results>
991
+
992
+ <think>Open around L190-200 to see percentage.</think>
993
+
994
+ <|DSML|function_calls>
995
+ <|DSML|invoke name="open">
996
+ <|DSML|parameter name="cursor" string="false">4</|DSML|parameter>
997
+ <|DSML|parameter name="loc" string="false">185</|DSML|parameter>
998
+ </|DSML|invoke>
999
+ </|DSML|function_calls><|end▁of▁sentence|>
1000
+
1001
+ <function_results>
1002
+ <result>[18] www.annualreports.com (https://www.annualreports.com/HostedData/AnnualReportArchive/d/defenders-wildlife_2021.pdf)
1003
+ **viewing lines [185 - 232] of 261**
1004
+
1005
+ L185: devastating water pollution that is killing the seagrass and causing manatees to
1006
+ L186: starve. DAVID TES | SAM FRENZY DRAW DEFENDERS.ORG 11 ===== Page 12 ===== In
1007
+ L187: 2021, Defenders of Wildlife’s nearly 2.2 million members and supporters
1008
+ L188: contributed more than $49 million for wildlife and wildlife habitat. This figure
1009
+ L189: includes all those who generously donated their time and expertise. The
1010
+ L190: steadfast support of our donors allows Defenders to sustain our program and
1011
+ L191: public education efforts in the field, the courts and on Capitol Hill. 2021
1012
+ L192: SOURCES OF FUNDS Grants and contributions $29,057 Bequests, trusts and split
1013
+ L193: interests $7,692 Income from investments, annuity reserve funds and trusts
1014
+ L194: $3,354 Royalties and other income $3,576 Contributed services $6,140 Total
1015
+ L195: Revenue $49,819 USES OF FUNDS Biodiversity conservation $22,420 Constituency
1016
+ L196: mobilization $16,324 Fundraising $1,211 Management and general $5,865 Total
1017
+ L197: Expenses $45,820 Change in net assets $3,999 Net assets, start of year $41,145
1018
+ L198: Net Assets, End of the Year $45,144 Dollars are in thousands. 12 DEFENDERS OF
1019
+ L199: WILDLIFE Grants and contributions 58% Income from investments 7% Requests,
1020
+ L200: trusts and split interests 15% Royalties and other income 7% Contributed
1021
+ L201: services 12% Program and support services 97% Fundraising 3% ===== Page 13 =====
1022
+ L202: DEFENDERS.ORG 13 ===== Page 14 ===== John Dayton, Esq.* Chair Texas Sheryl
1023
+ L203: Sachs* Vice Chair Connecticut Whitfield Bailey* Secretary Tennessee Mark Caylor*
1024
+ L204: Treasurer District of Columbia Sylvia Baca* California Donald Barry, Esq.
1025
+ L205: Oregon Dinah Bear, Esq. Arizona Kim O’Keefe Beck, Ph.D California Gabriela
1026
+ L206: Chavarria, Ph.D Washington Jamie Rappaport Clark* President & CEO Virginia
1027
+ L207: Caroline D. Gabel Maryland Keith M. Harper, Esq. Maryland John Donovan, Esq.
1028
+ L208: Montana and California James M. Hecker, Esq. Virginia Judith Ponsikoff, Ph.D*
1029
+ L209: California Terry L. Root, Ph.D Florida BOARD EMERITUS Terry C. Pelster, Esq. New
1030
+ L210: York *Executive Committee member* ST A F F & A D V I S O R S SCIENCE ADVISORS
1031
+ L211: Gabriela Chavarria, Ph.D Burke Museum, University of Washington Leah Gerber,
1032
+ L212: Ph.D Arizona State University Kathayoon Khalil, Ph.D Oregon Zoo Rurik List, Ph.D
1033
+ L213: Universidad Autónoma Metropolitana-Lerma Barry Noon, Ph.D Colorado State
1034
+ L214: University Terry L. Root, Ph.D Stanford University Mark Schwartz, Ph.D
1035
+ L215: University of California-Davis David S. Wilcox, Ph.D Princeton University Rae
1036
+ L216: Wynn-Grant, Ph.D National Geographic Society SENIOR STAFF Jamie Rappaport Clark
1037
+ L217: President & CEO Thu Pham Chief of Staff Renee Stone Senior Vice President,
1038
+ L218: Conservation Programs & General Counsel James Stefan Senior Vice President,
1039
+ L219: Operations Matt Braughler Vice President, Philanthropy Rachel Brittin Vice
1040
+ L220: President, Communications Shawn Cantrell Vice President, Field Conservation
1041
+ L221: Programs Robert L. Dewey Vice President, Government Relations & External Affairs
1042
+ L222: Monica Goldberg Vice President, Landscape Conservation Sharon Morris-Smith Vice
1043
+ L223: President of Human Resources Elizabeth Nichols Vice President, Finance/CFO
1044
+ L224: Anthony Pegues Vice President, Diversity, Equity, Inclusion, Justice Lindsay
1045
+ L225: Rosa, Ph.D Director, Center for Conservation Innovation Michael P. Senatore Vice
1046
+ L226: President, Conservation Law Brandace Swimley Vice President, Integrated
1047
+ L227: Marketing 14 DEFENDERS OF WILDLIFE ===== Page 15 ===== THERE ARE MANY WAYS TO
1048
+ L228: SUPPORT DEFENDERS OF WILDLIFE and protect our imperiled wildlife. To learn more
1049
+ L229: or donate, contact us at 1-800-385-9712, visit defenders.org/donate or choose
1050
+ L230: one of the following options. ➤ Transfer appreciated assets directly to
1051
+ L231: Defenders and maximize potential tax benefits. Defenders.org/donate-stocks-help-
1052
+ L232: save-wildlife ➤ Become a Leadership Council member with a single donation of </result>
1053
+ </function_results>
1054
+
1055
+ <think>Thus everything matches.
1056
+
1057
+ Thus answer: Terry C. Pelster.
1058
+
1059
+ Now we need to output only the name.
1060
+
1061
+ Thus final answer: Terry C. Pelster.
1062
+
1063
+ But perhaps the question expects "Terry Pelster". However, the document includes middle initial "C." It might be better to include the full name as listed: "Terry C. Pelster". The instruction "Name only, no titles" suggests we should not include "Esq." or "Board Emeritus". But including middle initial is likely okay.
1064
+
1065
+ To be safe, we could output "Terry C. Pelster". Let's see similar puzzles: often they expect the full name as appears in the document.
1066
+
1067
+ Thus answer: Terry C. Pelster.
1068
+
1069
+ Now we will provide final answer.</think>Terry C. Pelster<|end▁of▁sentence|>
generation_config.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "transformers_version": "4.57.1"
4
+ }
inference/README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeepSeek V3.2
2
+
3
+ First convert huggingface model weights to the the format required by our inference demo. Set `MP` to match your available GPU count:
4
+ ```bash
5
+ cd inference
6
+ export EXPERTS=256
7
+ python convert.py --hf-ckpt-path ${HF_CKPT_PATH} --save-path ${SAVE_PATH} --n-experts ${EXPERTS} --model-parallel ${MP}
8
+ ```
9
+
10
+ Launch the interactive chat interface and start exploring DeepSeek's capabilities:
11
+ ```bash
12
+ export CONFIG=config_671B_v3.2.json
13
+ torchrun --nproc-per-node ${MP} generate.py --ckpt-path ${SAVE_PATH} --config ${CONFIG} --interactive
14
+ ```
inference/config_671B_v3.2.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 129280,
3
+ "dim": 7168,
4
+ "inter_dim": 18432,
5
+ "moe_inter_dim": 2048,
6
+ "n_layers": 61,
7
+ "n_dense_layers": 3,
8
+ "n_heads": 128,
9
+ "n_routed_experts": 256,
10
+ "n_shared_experts": 1,
11
+ "n_activated_experts": 8,
12
+ "n_expert_groups": 8,
13
+ "n_limited_groups": 4,
14
+ "route_scale": 2.5,
15
+ "score_func": "sigmoid",
16
+ "q_lora_rank": 1536,
17
+ "kv_lora_rank": 512,
18
+ "qk_nope_head_dim": 128,
19
+ "qk_rope_head_dim": 64,
20
+ "v_head_dim": 128,
21
+ "dtype": "fp8",
22
+ "scale_fmt": "ue8m0",
23
+ "index_n_heads": 64,
24
+ "index_head_dim": 128,
25
+ "index_topk": 2048
26
+ }
inference/convert.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ from argparse import ArgumentParser
4
+ from glob import glob
5
+ from tqdm import tqdm, trange
6
+
7
+ import torch
8
+ from safetensors.torch import safe_open, save_file
9
+
10
+
11
+ mapping = {
12
+ "embed_tokens": ("embed", 0),
13
+ "input_layernorm": ("attn_norm", None),
14
+ "post_attention_layernorm": ("ffn_norm", None),
15
+ "q_proj": ("wq", 0),
16
+ "q_a_proj": ("wq_a", None),
17
+ "q_a_layernorm": ("q_norm", None),
18
+ "q_b_proj": ("wq_b", 0),
19
+ "kv_a_proj_with_mqa": ("wkv_a", None),
20
+ "kv_a_layernorm": ("kv_norm", None),
21
+ "kv_b_proj": ("wkv_b", 0),
22
+ "o_proj": ("wo", 1),
23
+ "gate": ("gate", None),
24
+ "gate_proj": ("w1", 0),
25
+ "down_proj": ("w2", 1),
26
+ "up_proj": ("w3", 0),
27
+ "norm": ("norm", None),
28
+ "lm_head": ("head", 0),
29
+ "scale": ("scale", None),
30
+ "wq_b": ("wq_b", None),
31
+ "wk": ("wk", None),
32
+ "k_norm": ("k_norm", None),
33
+ "weights_proj": ("weights_proj", None),
34
+ }
35
+
36
+
37
+ def main(hf_ckpt_path, save_path, n_experts, mp):
38
+ """
39
+ Converts and saves model checkpoint files into a specified format.
40
+
41
+ Args:
42
+ hf_ckpt_path (str): Path to the directory containing the input checkpoint files.
43
+ save_path (str): Path to the directory where the converted checkpoint files will be saved.
44
+ n_experts (int): Total number of experts in the model.
45
+ mp (int): Model parallelism factor.
46
+
47
+ Returns:
48
+ None
49
+ """
50
+ torch.set_num_threads(8)
51
+ n_local_experts = n_experts // mp
52
+ state_dicts = [{} for _ in range(mp)]
53
+
54
+ for file_path in tqdm(glob(os.path.join(hf_ckpt_path, "*.safetensors"))):
55
+ with safe_open(file_path, framework="pt", device="cpu") as f:
56
+ for name in f.keys():
57
+ if "model.layers.61" in name:
58
+ continue
59
+ param: torch.Tensor = f.get_tensor(name)
60
+ if name.startswith("model."):
61
+ name = name[len("model."):]
62
+ name = name.replace("self_attn", "attn")
63
+ name = name.replace("mlp", "ffn")
64
+ name = name.replace("weight_scale_inv", "scale")
65
+ name = name.replace("e_score_correction_bias", "bias")
66
+ key = name.split(".")[-2]
67
+ assert key in mapping, f"Key {key} not found in mapping"
68
+ new_key, dim = mapping[key]
69
+ name = name.replace(key, new_key)
70
+ for i in range(mp):
71
+ new_param = param
72
+ if "experts" in name and "shared_experts" not in name:
73
+ idx = int(name.split(".")[-3])
74
+ if idx < i * n_local_experts or idx >= (i + 1) * n_local_experts:
75
+ continue
76
+ elif dim is not None:
77
+ assert param.size(dim) % mp == 0, f"Dimension {dim} must be divisible by {mp}"
78
+ shard_size = param.size(dim) // mp
79
+ new_param = param.narrow(dim, i * shard_size, shard_size).contiguous()
80
+ state_dicts[i][name] = new_param
81
+
82
+ os.makedirs(save_path, exist_ok=True)
83
+
84
+ for i in trange(mp):
85
+ save_file(state_dicts[i], os.path.join(save_path, f"model{i}-mp{mp}.safetensors"))
86
+
87
+ for file_path in glob(os.path.join(hf_ckpt_path, "*token*")):
88
+ new_file_path = os.path.join(save_path, os.path.basename(file_path))
89
+ shutil.copyfile(file_path, new_file_path)
90
+
91
+
92
+ if __name__ == "__main__":
93
+ parser = ArgumentParser()
94
+ parser.add_argument("--hf-ckpt-path", type=str, required=True)
95
+ parser.add_argument("--save-path", type=str, required=True)
96
+ parser.add_argument("--n-experts", type=int, required=True)
97
+ parser.add_argument("--model-parallel", type=int, required=True)
98
+ args = parser.parse_args()
99
+ assert args.n_experts % args.model_parallel == 0, "Number of experts must be divisible by model parallelism"
100
+ main(args.hf_ckpt_path, args.save_path, args.n_experts, args.model_parallel)
inference/generate.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from argparse import ArgumentParser
4
+ from typing import List
5
+
6
+ import torch
7
+ import torch.distributed as dist
8
+ from transformers import AutoTokenizer
9
+ from safetensors.torch import load_model
10
+
11
+ from model import Transformer, ModelArgs
12
+
13
+
14
+ def sample(logits, temperature: float = 1.0):
15
+ """
16
+ Samples a token from the logits using temperature scaling.
17
+
18
+ Args:
19
+ logits (torch.Tensor): The logits tensor for token predictions.
20
+ temperature (float, optional): Temperature for scaling logits. Defaults to 1.0.
21
+
22
+ Returns:
23
+ torch.Tensor: The sampled token.
24
+ """
25
+ logits = logits / max(temperature, 1e-5)
26
+ probs = torch.softmax(logits, dim=-1, dtype=torch.float32)
27
+ return probs.div_(torch.empty_like(probs).exponential_(1)).argmax(dim=-1)
28
+
29
+
30
+ @torch.inference_mode()
31
+ def generate(
32
+ model: Transformer,
33
+ prompt_tokens: List[List[int]],
34
+ max_new_tokens: int,
35
+ eos_id: int,
36
+ temperature: float = 1.0
37
+ ) -> List[List[int]]:
38
+ """
39
+ Generates new tokens based on the given prompt tokens using the specified model.
40
+
41
+ Args:
42
+ model (Transformer): The transformer model used for token generation.
43
+ prompt_tokens (List[List[int]]): A list of lists containing the prompt tokens for each sequence.
44
+ max_new_tokens (int): The maximum number of new tokens to generate.
45
+ eos_id (int): The end-of-sequence token ID.
46
+ temperature (float, optional): The temperature value for sampling. Defaults to 1.0.
47
+
48
+ Returns:
49
+ List[List[int]]: A list of lists containing the generated tokens for each sequence.
50
+ """
51
+ prompt_lens = [len(t) for t in prompt_tokens]
52
+ assert max(prompt_lens) <= model.max_seq_len, f"Prompt length exceeds model maximum sequence length (max_seq_len={model.max_seq_len})"
53
+ total_len = min(model.max_seq_len, max_new_tokens + max(prompt_lens))
54
+ tokens = torch.full((len(prompt_tokens), total_len), -1, dtype=torch.long, device="cuda")
55
+ for i, t in enumerate(prompt_tokens):
56
+ tokens[i, :len(t)] = torch.tensor(t, dtype=torch.long, device="cuda")
57
+ prev_pos = 0
58
+ finished = torch.tensor([False] * len(prompt_tokens), device="cuda")
59
+ prompt_mask = tokens != -1
60
+ for cur_pos in range(min(prompt_lens), total_len):
61
+ logits = model.forward(tokens[:, prev_pos:cur_pos], prev_pos)
62
+ if temperature > 0:
63
+ next_token = sample(logits, temperature)
64
+ else:
65
+ next_token = logits.argmax(dim=-1)
66
+ next_token = torch.where(prompt_mask[:, cur_pos], tokens[:, cur_pos], next_token)
67
+ tokens[:, cur_pos] = next_token
68
+ finished |= torch.logical_and(~prompt_mask[:, cur_pos], next_token == eos_id)
69
+ prev_pos = cur_pos
70
+ if finished.all():
71
+ break
72
+ completion_tokens = []
73
+ for i, toks in enumerate(tokens.tolist()):
74
+ toks = toks[prompt_lens[i]:prompt_lens[i]+max_new_tokens]
75
+ if eos_id in toks:
76
+ toks = toks[:toks.index(eos_id)]
77
+ completion_tokens.append(toks)
78
+ return completion_tokens
79
+
80
+
81
+ def main(
82
+ ckpt_path: str,
83
+ config: str,
84
+ input_file: str = "",
85
+ interactive: bool = True,
86
+ max_new_tokens: int = 100,
87
+ temperature: float = 1.0,
88
+ ) -> None:
89
+ """
90
+ Main function to load the model and perform interactive or batch text generation.
91
+
92
+ Args:
93
+ ckpt_path (str): Path to the model checkpoint directory.
94
+ config (str): Path to the model configuration file.
95
+ input_file (str, optional): Path to a file containing input prompts. Defaults to "".
96
+ interactive (bool, optional): Whether to run in interactive mode. Defaults to True.
97
+ max_new_tokens (int, optional): Maximum number of new tokens to generate. Defaults to 100.
98
+ temperature (float, optional): Temperature for sampling. Defaults to 1.0.
99
+ """
100
+ world_size = int(os.getenv("WORLD_SIZE", "1"))
101
+ rank = int(os.getenv("RANK", "0"))
102
+ local_rank = int(os.getenv("LOCAL_RANK", "0"))
103
+ if world_size > 1:
104
+ dist.init_process_group("nccl")
105
+ global print
106
+ if rank != 0:
107
+ print = lambda *_, **__: None
108
+ torch.cuda.set_device(local_rank)
109
+ torch.set_default_dtype(torch.bfloat16)
110
+ torch.set_num_threads(8)
111
+ torch.manual_seed(33377335)
112
+ with open(config) as f:
113
+ args = ModelArgs(**json.load(f))
114
+ print(args)
115
+ with torch.device("cuda"):
116
+ model = Transformer(args)
117
+ tokenizer = AutoTokenizer.from_pretrained(ckpt_path)
118
+ print("load model")
119
+ load_model(model, os.path.join(ckpt_path, f"model{rank}-mp{world_size}.safetensors"))
120
+ print("I'm DeepSeek 👋")
121
+
122
+ if interactive:
123
+ messages = []
124
+ while True:
125
+ if world_size == 1:
126
+ prompt = input(">>> ")
127
+ elif rank == 0:
128
+ prompt = input(">>> ")
129
+ objects = [prompt]
130
+ dist.broadcast_object_list(objects, 0)
131
+ else:
132
+ objects = [None]
133
+ dist.broadcast_object_list(objects, 0)
134
+ prompt = objects[0]
135
+ if prompt == "/exit":
136
+ break
137
+ elif prompt == "/clear":
138
+ messages.clear()
139
+ continue
140
+ messages.append({"role": "user", "content": prompt})
141
+ prompt_tokens = tokenizer.apply_chat_template(messages, add_generation_prompt=True)
142
+ completion_tokens = generate(model, [prompt_tokens], max_new_tokens, tokenizer.eos_token_id, temperature)
143
+ completion = tokenizer.decode(completion_tokens[0], skip_special_tokens=True)
144
+ print(completion)
145
+ messages.append({"role": "assistant", "content": completion})
146
+ else:
147
+ with open(input_file) as f:
148
+ prompts = f.read().split("\n\n")
149
+ assert len(prompts) <= args.max_batch_size, f"Number of prompts exceeds maximum batch size ({args.max_batch_size})"
150
+ prompt_tokens = [tokenizer.apply_chat_template([{"role": "user", "content": prompt}], add_generation_prompt=True) for prompt in prompts]
151
+ completion_tokens = generate(model, prompt_tokens, max_new_tokens, tokenizer.eos_token_id, temperature)
152
+ completions = tokenizer.batch_decode(completion_tokens, skip_special_tokens=True)
153
+ for prompt, completion in zip(prompts, completions):
154
+ print("Prompt:", prompt)
155
+ print("Completion:", completion)
156
+ print()
157
+
158
+ if world_size > 1:
159
+ dist.destroy_process_group()
160
+
161
+
162
+ if __name__ == "__main__":
163
+ """
164
+ Command-line interface for distributed text generation.
165
+
166
+ Arguments:
167
+ --ckpt-path (str): Path to the model checkpoint directory.
168
+ --config (str): Path to the model configuration file.
169
+ --input-file (str, optional): File containing prompts for batch processing.
170
+ --interactive (bool, optional): Enable interactive mode for generating text.
171
+ --max-new-tokens (int, optional): Maximum number of new tokens to generate. Defaults to 200.
172
+ --temperature (float, optional): Temperature for sampling. Defaults to 0.2.
173
+
174
+ Raises:
175
+ AssertionError: If neither input-file nor interactive mode is specified.
176
+ """
177
+ parser = ArgumentParser()
178
+ parser.add_argument("--ckpt-path", type=str, required=True)
179
+ parser.add_argument("--config", type=str, required=True)
180
+ parser.add_argument("--input-file", type=str, default="")
181
+ parser.add_argument("--interactive", action="store_true")
182
+ parser.add_argument("--max-new-tokens", type=int, default=200)
183
+ parser.add_argument("--temperature", type=float, default=0.6)
184
+ args = parser.parse_args()
185
+ assert args.input_file or args.interactive, "Either input-file or interactive mode must be specified"
186
+ main(args.ckpt_path, args.config, args.input_file, args.interactive, args.max_new_tokens, args.temperature)
inference/kernel.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import tilelang
3
+ import tilelang.language as T
4
+ from typing import Tuple, Optional
5
+
6
+
7
+ tilelang.set_log_level("WARNING")
8
+
9
+ pass_configs = {
10
+ tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True,
11
+ tilelang.PassConfigKey.TL_DISABLE_TMA_LOWER: True,
12
+ tilelang.PassConfigKey.TL_DISABLE_FAST_MATH: True,
13
+ }
14
+
15
+ FP8 = "float8_e4m3"
16
+ BF16 = "bfloat16"
17
+ FP32 = "float32"
18
+
19
+
20
+ def fast_log2_ceil(x):
21
+ bits_x = T.reinterpret("uint32", x)
22
+ exp_x = (bits_x >> 23) & 0xFF
23
+ man_bits = bits_x & ((1 << 23) - 1)
24
+ return T.Cast("int32", exp_x - 127 + T.if_then_else(man_bits != 0, 1, 0))
25
+
26
+
27
+ def fast_pow2(x):
28
+ bits_x = (x + 127) << 23
29
+ return T.reinterpret("float32", bits_x)
30
+
31
+
32
+ def fast_round_scale(amax, fp8_max_inv):
33
+ return fast_pow2(fast_log2_ceil(amax * fp8_max_inv))
34
+
35
+
36
+ @tilelang.jit(pass_configs=pass_configs)
37
+ def act_quant_kernel(
38
+ N, in_dtype=BF16, out_dtype=FP8, scale_dtype=FP32, round_scale=False
39
+ ):
40
+ M = T.symbolic("M")
41
+ fp8_min = -448.0
42
+ fp8_max = 448.0
43
+ fp8_max_inv = 1 / fp8_max
44
+ num_stages = 0 if round_scale else 2
45
+ blk_m = 32
46
+ group_size = 128
47
+
48
+ @T.prim_func
49
+ def act_quant_kernel_(
50
+ X: T.Tensor[(M, N), in_dtype],
51
+ Y: T.Tensor[(M, N), out_dtype],
52
+ S: T.Tensor[(M, T.ceildiv(N, group_size)), scale_dtype],
53
+ ):
54
+ with T.Kernel(T.ceildiv(M, blk_m), T.ceildiv(N, group_size), threads=128) as (
55
+ pid_m,
56
+ pid_n,
57
+ ):
58
+ x_shared = T.alloc_shared((blk_m, group_size), in_dtype)
59
+ x_local = T.alloc_fragment((blk_m, group_size), in_dtype)
60
+ amax_local = T.alloc_fragment((blk_m,), scale_dtype)
61
+ s_local = T.alloc_fragment((blk_m,), scale_dtype)
62
+ y_local = T.alloc_fragment((blk_m, group_size), out_dtype)
63
+ y_shared = T.alloc_shared((blk_m, group_size), out_dtype)
64
+
65
+ for _ in T.Pipelined(1, num_stages=num_stages):
66
+ T.copy(X[pid_m * blk_m, pid_n * group_size], x_shared)
67
+ T.copy(x_shared, x_local)
68
+ T.reduce_absmax(x_local, amax_local, dim=1)
69
+ for i in T.Parallel(blk_m):
70
+ amax_local[i] = T.max(amax_local[i], 1e-4)
71
+ if round_scale:
72
+ s_local[i] = fast_round_scale(amax_local[i], fp8_max_inv)
73
+ else:
74
+ s_local[i] = amax_local[i] * fp8_max_inv
75
+ for i, j in T.Parallel(blk_m, group_size):
76
+ y_local[i, j] = T.clamp(
77
+ x_local[i, j] / s_local[i], fp8_min, fp8_max
78
+ )
79
+ for i in T.Parallel(blk_m):
80
+ S[pid_m * blk_m + i, pid_n] = s_local[i]
81
+ T.copy(y_local, y_shared)
82
+ T.copy(y_shared, Y[pid_m * blk_m, pid_n * group_size])
83
+
84
+ return act_quant_kernel_
85
+
86
+
87
+ def act_quant(
88
+ x: torch.Tensor, block_size: int = 128, scale_fmt: Optional[str] = None
89
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
90
+ """
91
+ Quantizes the input tensor `x` using block-wise quantization.
92
+
93
+ Args:
94
+ x (torch.Tensor): The input tensor to be quantized. Must be contiguous and its last dimension size must be divisible by `block_size`.
95
+ block_size (int, optional): The size of the blocks to be used for quantization. Default is 128.
96
+ scale_fmt (Optional[str], optional): The format of the scale. Default is None.
97
+ Returns:
98
+ Tuple[torch.Tensor, torch.Tensor]: A tuple containing:
99
+ - The quantized tensor with dtype `torch.float8_e4m3fn`.
100
+ - A tensor of scaling factors with dtype `torch.float32`.
101
+ """
102
+ assert x.is_contiguous(), "Input tensor must be contiguous"
103
+ assert x.size(-1) % block_size == 0, (
104
+ f"Last dimension size must be divisible by block_size (block_size={block_size})"
105
+ )
106
+ N = x.size(-1)
107
+ y = torch.empty_like(x, dtype=torch.float8_e4m3fn)
108
+ s = x.new_empty(*x.size()[:-1], N // block_size, dtype=torch.float32)
109
+ kernel = act_quant_kernel(N, round_scale=scale_fmt is not None)
110
+ kernel(x.view(-1, N), y.view(-1, N), s.view(-1, N // block_size))
111
+ return y, s
112
+
113
+
114
+ @tilelang.jit(pass_configs=pass_configs)
115
+ def fp8_gemm_kernel(N, K, out_dtype=BF16, accum_dtype="float32"):
116
+ assert out_dtype in [BF16, "float32"]
117
+
118
+ M = T.symbolic("M")
119
+ group_size = 128
120
+ block_M = 32
121
+ block_N = 128
122
+ block_K = 128
123
+
124
+ @T.prim_func
125
+ def fp8_gemm_kernel_(
126
+ A: T.Tensor[(M, K), FP8],
127
+ B: T.Tensor[(N, K), FP8],
128
+ C: T.Tensor[(M, N), out_dtype],
129
+ scales_a: T.Tensor[(M, T.ceildiv(K, group_size)), FP32],
130
+ scales_b: T.Tensor[(T.ceildiv(N, group_size), T.ceildiv(K, group_size)), FP32],
131
+ ):
132
+ with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=128) as (
133
+ bx,
134
+ by,
135
+ ):
136
+ A_shared = T.alloc_shared((block_M, block_K), FP8)
137
+ B_shared = T.alloc_shared((block_N, block_K), FP8)
138
+ C_shared = T.alloc_shared((block_M, block_N), out_dtype)
139
+ Scale_C_shared = T.alloc_shared((block_M), FP32)
140
+ C_local = T.alloc_fragment((block_M, block_N), accum_dtype)
141
+ C_local_accum = T.alloc_fragment((block_M, block_N), accum_dtype)
142
+
143
+ # Improve L2 Cache
144
+ T.use_swizzle(panel_size=10)
145
+
146
+ T.clear(C_local)
147
+ T.clear(C_local_accum)
148
+ K_iters = T.ceildiv(K, block_K)
149
+ for k in T.Pipelined(K_iters, num_stages=4):
150
+ # Load A into shared memory
151
+ T.copy(A[by * block_M, k * block_K], A_shared)
152
+ # Load B into shared memory
153
+ T.copy(B[bx * block_N, k * block_K], B_shared)
154
+ # Load scale into shared memory
155
+ Scale_B = scales_b[bx * block_N // group_size, k]
156
+ for i in T.Parallel(block_M):
157
+ Scale_C_shared[i] = scales_a[by * block_M + i, k] * Scale_B
158
+
159
+ T.gemm(A_shared, B_shared, C_local, transpose_B=True)
160
+ # Promote to enable 2xAcc
161
+ for i, j in T.Parallel(block_M, block_N):
162
+ C_local_accum[i, j] += C_local[i, j] * Scale_C_shared[i]
163
+ T.clear(C_local)
164
+ # TMA store
165
+ T.copy(C_local_accum, C_shared)
166
+ T.copy(C_shared, C[by * block_M, bx * block_N])
167
+
168
+ return fp8_gemm_kernel_
169
+
170
+
171
+ def fp8_gemm(
172
+ a: torch.Tensor, a_s: torch.Tensor, b: torch.Tensor, b_s: torch.Tensor
173
+ ) -> torch.Tensor:
174
+ """
175
+ Perform a matrix multiplication using FP8 precision.
176
+
177
+ Args:
178
+ a (torch.Tensor): The first input matrix, must be contiguous.
179
+ a_s (torch.Tensor): The scaling factor for the first input matrix, must be contiguous.
180
+ b (torch.Tensor): The second input matrix, must be contiguous.
181
+ b_s (torch.Tensor): The scaling factor for the second input matrix, must be contiguous.
182
+
183
+ Returns:
184
+ torch.Tensor: The result of the matrix multiplication.
185
+ """
186
+ assert a.is_contiguous() and b.is_contiguous(), "Input tensors must be contiguous"
187
+ assert a_s.is_contiguous() and b_s.is_contiguous(), (
188
+ "Scaling factor tensors must be contiguous"
189
+ )
190
+ K = a.size(-1)
191
+ M = a.numel() // K
192
+ N = b.size(0)
193
+ c = a.new_empty(*a.size()[:-1], N, dtype=torch.get_default_dtype())
194
+ kernel = fp8_gemm_kernel(N, K)
195
+ kernel(a.view(M, K), b, c.view(M, N), a_s.view(M, -1), b_s)
196
+ return c
197
+
198
+
199
+ @tilelang.jit(out_idx=[4], pass_configs=pass_configs)
200
+ def fp8_index_kernel(h: int, d: int):
201
+ b = T.symbolic("b")
202
+ m = T.symbolic("m")
203
+ n = T.symbolic("n")
204
+
205
+ blk_n1 = 512
206
+ blk_n2 = 128
207
+
208
+ @T.prim_func
209
+ def fp8_index_kernel_(
210
+ q: T.Tensor[(b, m, h, d), FP8],
211
+ q_s: T.Tensor[(b, m, h), FP32],
212
+ k: T.Tensor[(b, n, d), FP8],
213
+ k_s: T.Tensor[(b, n), FP32],
214
+ o: T.Tensor[(b, m, n), FP32],
215
+ ) -> None:
216
+ with T.Kernel(b, m, T.ceildiv(n, blk_n1)) as (i_b, i_m, i1_n):
217
+ q_smem = T.alloc_shared((h, d), FP8)
218
+ T.copy(q[i_b, i_m, 0, 0], q_smem)
219
+
220
+ q_s_frag = T.alloc_fragment(h, FP32)
221
+ T.copy(q_s[i_b, i_m, 0], q_s_frag)
222
+
223
+ for i2_n in T.Pipelined(blk_n1 // blk_n2, num_stages=2):
224
+ k_smem = T.alloc_shared((blk_n2, d), FP8)
225
+ T.copy(k[i_b, i1_n * blk_n1 + i2_n * blk_n2, 0], k_smem)
226
+
227
+ k_s_frag = T.alloc_fragment(blk_n2, FP32)
228
+ T.copy(k_s[i_b, i1_n * blk_n1 + i2_n * blk_n2], k_s_frag)
229
+
230
+ logits = T.alloc_fragment((blk_n2, h), FP32)
231
+ T.gemm(
232
+ k_smem,
233
+ q_smem,
234
+ logits,
235
+ transpose_A=False,
236
+ transpose_B=True,
237
+ clear_accum=True,
238
+ )
239
+
240
+ for i_h, i3_n in T.Parallel(h, blk_n2):
241
+ logits[i3_n, i_h] = T.max(logits[i3_n, i_h], 0) * q_s_frag[i_h]
242
+
243
+ logits_sum = T.alloc_fragment(blk_n2, FP32)
244
+ T.reduce_sum(logits, logits_sum, dim=1)
245
+
246
+ for i3_n in T.Parallel(blk_n2):
247
+ logits_sum[i3_n] *= k_s_frag[i3_n]
248
+
249
+ T.copy(logits_sum, o[i_b, i_m, i1_n * blk_n1 + i2_n * blk_n2])
250
+
251
+ return fp8_index_kernel_
252
+
253
+
254
+ def fp8_index(
255
+ q: torch.Tensor,
256
+ q_s: torch.Tensor,
257
+ k: torch.Tensor,
258
+ k_s: torch.Tensor,
259
+ ) -> torch.Tensor:
260
+ """
261
+ Perform index score using FP8 precision.
262
+
263
+ Args:
264
+ q (torch.Tensor): The Q tensor, must be contiguous.
265
+ q_s (torch.Tensor): The scaling factor for Q (float), must be contiguous.
266
+ k (torch.Tensor): The K tensor, must be contiguous.
267
+ k_s (torch.Tensor): The scaling factor for K (e8m0 here), must be contiguous.
268
+
269
+ fp8 q @ fp8 k -> fp32 logits
270
+ relu(fp32 logits) * q_s (weights) -> fp32 logits
271
+ fp32 logits -> fp32 logits_sum
272
+ fp32 logits_sum * k_s (e8m0) -> fp32 index_score
273
+ """
274
+ return fp8_index_kernel(q.shape[2], q.shape[3])(q, q_s, k, k_s)
inference/model.py ADDED
@@ -0,0 +1,923 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from dataclasses import dataclass
3
+ from typing import Tuple, Optional, Literal
4
+
5
+ import torch
6
+ from torch import nn
7
+ import torch.nn.functional as F
8
+ import torch.distributed as dist
9
+
10
+ from kernel import act_quant, fp8_gemm, fp8_index
11
+
12
+
13
+ world_size = 1
14
+ rank = 0
15
+ block_size = 128
16
+
17
+ @dataclass
18
+ class ModelArgs:
19
+ """
20
+ Data class for defining model arguments and hyperparameters.
21
+
22
+ Attributes:
23
+ max_batch_size (int): Maximum batch size.
24
+ max_seq_len (int): Maximum sequence length.
25
+ dtype (Literal["bf16", "fp8"]): Data type for computations.
26
+ scale_fmt (Optional[str]): Format for quantization scale.
27
+ vocab_size (int): Vocabulary size.
28
+ dim (int): Model dimension.
29
+ inter_dim (int): Intermediate dimension for MLP layers.
30
+ moe_inter_dim (int): Intermediate dimension for MoE layers.
31
+ n_layers (int): Number of transformer layers.
32
+ n_dense_layers (int): Number of dense layers in the model.
33
+ n_heads (int): Number of attention heads.
34
+ n_routed_experts (int): Number of routed experts for MoE layers.
35
+ n_shared_experts (int): Number of shared experts for MoE layers.
36
+ n_activated_experts (int): Number of activated experts in MoE layers.
37
+ n_expert_groups (int): Number of expert groups.
38
+ n_limited_groups (int): Number of limited groups for MoE routing.
39
+ score_func (Literal["softmax", "sigmoid"]): Scoring function for MoE routing.
40
+ route_scale (float): Scaling factor for routing scores.
41
+ q_lora_rank (int): LoRA rank for query projections.
42
+ kv_lora_rank (int): LoRA rank for key-value projections.
43
+ qk_nope_head_dim (int): Dimension for query-key projections without positional embeddings.
44
+ qk_rope_head_dim (int): Dimension for query-key projections with rotary embeddings.
45
+ v_head_dim (int): Dimension for value projections.
46
+ original_seq_len (int): Original sequence length.
47
+ rope_theta (float): Base for rotary positional encoding.
48
+ rope_factor (float): Scaling factor for extended sequence lengths.
49
+ beta_fast (int): Fast beta correction factor.
50
+ beta_slow (int): Slow beta correction factor.
51
+ mscale (float): Scaling factor for extended attention.
52
+ index_head_dim (int): Dimension for index head.
53
+ index_topk (int): Top-k for index head.
54
+ """
55
+ max_batch_size: int = 8
56
+ max_seq_len: int = 4096 * 4
57
+ dtype: Literal["bf16", "fp8"] = "bf16"
58
+ scale_fmt: Optional[str] = None
59
+ vocab_size: int = 102400
60
+ dim: int = 2048
61
+ inter_dim: int = 10944
62
+ moe_inter_dim: int = 1408
63
+ n_layers: int = 27
64
+ n_dense_layers: int = 1
65
+ n_heads: int = 16
66
+ # moe
67
+ n_routed_experts: int = 64
68
+ n_shared_experts: int = 2
69
+ n_activated_experts: int = 6
70
+ n_expert_groups: int = 1
71
+ n_limited_groups: int = 1
72
+ score_func: Literal["softmax", "sigmoid"] = "softmax"
73
+ route_scale: float = 1.
74
+ # mla
75
+ q_lora_rank: int = 0
76
+ kv_lora_rank: int = 512
77
+ qk_nope_head_dim: int = 128
78
+ qk_rope_head_dim: int = 64
79
+ v_head_dim: int = 128
80
+ # yarn
81
+ original_seq_len: int = 4096
82
+ rope_theta: float = 10000.0
83
+ rope_factor: float = 40
84
+ beta_fast: int = 32
85
+ beta_slow: int = 1
86
+ mscale: float = 1.
87
+ # index
88
+ index_n_heads: int = 64
89
+ index_head_dim: int = 128
90
+ index_topk: int = 2048
91
+
92
+ class ParallelEmbedding(nn.Module):
93
+ """
94
+ Embedding layer with parallelism support across distributed processes.
95
+
96
+ Args:
97
+ vocab_size (int): Vocabulary size.
98
+ dim (int): Embedding dimension.
99
+ """
100
+ def __init__(self, vocab_size: int, dim: int):
101
+ super().__init__()
102
+ self.vocab_size = vocab_size
103
+ self.dim = dim
104
+ assert vocab_size % world_size == 0, f"Vocabulary size must be divisible by world size (world_size={world_size})"
105
+ self.part_vocab_size = (vocab_size // world_size)
106
+ self.vocab_start_idx = rank * self.part_vocab_size
107
+ self.vocab_end_idx = self.vocab_start_idx + self.part_vocab_size
108
+ self.weight = nn.Parameter(torch.empty(self.part_vocab_size, self.dim))
109
+
110
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
111
+ """
112
+ Forward pass for parallel embedding layer.
113
+
114
+ Args:
115
+ x (torch.Tensor): Input tensor containing token indices.
116
+
117
+ Returns:
118
+ torch.Tensor: Embedded representations.
119
+
120
+ Raises:
121
+ ValueError: If `world_size` is not defined.
122
+ """
123
+ if world_size > 1:
124
+ mask = (x < self.vocab_start_idx) | (x >= self.vocab_end_idx)
125
+ x = x - self.vocab_start_idx
126
+ x[mask] = 0
127
+ y = F.embedding(x, self.weight)
128
+ if world_size > 1:
129
+ y[mask] = 0
130
+ dist.all_reduce(y)
131
+ return y
132
+
133
+
134
+ def linear(x: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor] = None,
135
+ scale_fmt: Optional[str] = None) -> torch.Tensor:
136
+ """
137
+ Applies a linear transformation to the incoming data: y = xA^T + b.
138
+ This function supports specialized implementations based on quantization
139
+ and tensor formats.
140
+
141
+ Args:
142
+ x (torch.Tensor): The input tensor.
143
+ weight (torch.Tensor): The weight tensor. It may be quantized and
144
+ requires dequantization for certain cases.
145
+ bias (Optional[torch.Tensor]): The bias tensor to be added. Default is None.
146
+ scale_fmt (Optional[str]): The format of scaling factors.
147
+
148
+ Returns:
149
+ torch.Tensor: The result of the linear transformation, which may involve
150
+ quantization-aware computations depending on the input parameters.
151
+
152
+ Notes:
153
+ - If `weight` is quantized (e.g., `element_size() == 1`), a dequantized version
154
+ is used for computation.
155
+ - For other cases, the function applies quantization to `x` and uses `fp8_gemm` for computation.
156
+ """
157
+ assert bias is None
158
+
159
+ if weight.dtype != torch.float8_e4m3fn:
160
+ return F.linear(x, weight)
161
+ else:
162
+ x, scale = act_quant(x, block_size, scale_fmt)
163
+ return fp8_gemm(x, scale, weight, weight.scale)
164
+
165
+
166
+ class Linear(nn.Module):
167
+ """
168
+ Custom linear layer with support for quantized weights and optional bias.
169
+
170
+ Args:
171
+ in_features (int): Number of input features.
172
+ out_features (int): Number of output features.
173
+ bias (bool): Whether to include a bias term. Defaults to False.
174
+ dtype (optional): Data type for the layer. Defaults to `torch.bfloat16`.
175
+ """
176
+ dtype = torch.bfloat16
177
+ scale_fmt: Optional[str] = None
178
+
179
+ def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype = None):
180
+ super().__init__()
181
+ self.in_features = in_features
182
+ self.out_features = out_features
183
+ self.weight = nn.Parameter(torch.empty(out_features, in_features, dtype=dtype or Linear.dtype))
184
+ if self.weight.element_size() == 1:
185
+ scale_out_features = (out_features + block_size - 1) // block_size
186
+ scale_in_features = (in_features + block_size - 1) // block_size
187
+ self.weight.scale = self.scale = nn.Parameter(torch.empty(scale_out_features, scale_in_features, dtype=torch.float32))
188
+ else:
189
+ self.register_parameter("scale", None)
190
+ if bias:
191
+ self.bias = nn.Parameter(torch.empty(out_features))
192
+ else:
193
+ self.register_parameter("bias", None)
194
+
195
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
196
+ """
197
+ Forward pass for the custom linear layer.
198
+
199
+ Args:
200
+ x (torch.Tensor): Input tensor.
201
+
202
+ Returns:
203
+ torch.Tensor: Transformed tensor after linear computation.
204
+ """
205
+ return linear(x, self.weight, self.bias, self.scale_fmt)
206
+
207
+
208
+ class ColumnParallelLinear(Linear):
209
+ """
210
+ Linear layer with column parallelism, splitting output features across distributed processes.
211
+
212
+ Args:
213
+ in_features (int): Number of input features.
214
+ out_features (int): Total number of output features.
215
+ bias (bool): Whether to include a bias term. Defaults to False.
216
+ dtype (optional): Data type for the layer. Defaults to `torch.bfloat16`.
217
+ """
218
+ def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype = None):
219
+ assert out_features % world_size == 0, f"Output features must be divisible by world size (world_size={world_size})"
220
+ self.part_out_features = out_features // world_size
221
+ super().__init__(in_features, self.part_out_features, bias, dtype)
222
+
223
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
224
+ """
225
+ Forward pass for column parallel linear layer.
226
+
227
+ Args:
228
+ x (torch.Tensor): Input tensor.
229
+
230
+ Returns:
231
+ torch.Tensor: Transformed tensor with column-parallel computation.
232
+ """
233
+ y = linear(x, self.weight, self.bias, self.scale_fmt)
234
+ return y
235
+
236
+
237
+ class RowParallelLinear(Linear):
238
+ """
239
+ Linear layer with row parallelism, splitting input features across distributed processes.
240
+
241
+ Args:
242
+ in_features (int): Total number of input features.
243
+ out_features (int): Number of output features.
244
+ bias (bool): Whether to include a bias term. Defaults to False.
245
+ dtype (optional): Data type for the layer. Defaults to `torch.bfloat16`.
246
+ """
247
+ def __init__(self, in_features: int, out_features: int, bias: bool = False, reduce_output = True, dtype = None):
248
+ assert in_features % world_size == 0, f"Input features must be divisible by world size (world_size={world_size})"
249
+ self.part_in_features = in_features // world_size
250
+ self.reduce_output = reduce_output
251
+ super().__init__(self.part_in_features, out_features, bias, dtype)
252
+
253
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
254
+ """
255
+ Forward pass for row parallel linear layer.
256
+
257
+ Args:
258
+ x (torch.Tensor): Input tensor.
259
+
260
+ Returns:
261
+ torch.Tensor: Transformed tensor with row-parallel computation.
262
+ """
263
+ y = linear(x, self.weight, None, self.scale_fmt)
264
+ if self.reduce_output and world_size > 1:
265
+ y = y.float()
266
+ dist.all_reduce(y)
267
+ if self.bias is not None:
268
+ y += self.bias
269
+ return y.type_as(x)
270
+
271
+
272
+ class RMSNorm(nn.Module):
273
+ """
274
+ Root Mean Square Layer Normalization (RMSNorm).
275
+
276
+ Args:
277
+ dim (int): Dimension of the input tensor.
278
+ eps (float): Epsilon value for numerical stability. Defaults to 1e-6.
279
+ """
280
+ def __init__(self, dim: int, eps: float = 1e-6):
281
+ super().__init__()
282
+ self.dim = dim
283
+ self.eps = eps
284
+ self.weight = nn.Parameter(torch.ones(dim, dtype=torch.float32))
285
+
286
+ def forward(self, x: torch.Tensor, residual: Optional[torch.Tensor] = None):
287
+ """
288
+ Forward pass for RMSNorm.
289
+
290
+ Args:
291
+ x (torch.Tensor): Input tensor.
292
+
293
+ Returns:
294
+ torch.Tensor: Normalized tensor with the same shape as input.
295
+ """
296
+ dtype = x.dtype
297
+ if residual is None:
298
+ x = x.float()
299
+ var = x.pow(2).mean(-1, keepdim=True)
300
+ x = x * torch.rsqrt(var + self.eps)
301
+ return (self.weight * x).to(dtype)
302
+ else:
303
+ x = residual = x.float() + residual.float()
304
+ var = x.pow(2).mean(-1, keepdim=True)
305
+ x = x * torch.rsqrt(var + self.eps)
306
+ return (self.weight * x).to(dtype), residual.to(dtype)
307
+
308
+
309
+ class LayerNorm(nn.Module):
310
+ """
311
+ Layer Normalization.
312
+ """
313
+ def __init__(self, dim: int, eps: float = 1e-6):
314
+ super().__init__()
315
+ self.dim = dim
316
+ self.eps = eps
317
+ self.weight = nn.Parameter(torch.ones(dim, dtype=torch.float32))
318
+ self.bias = nn.Parameter(torch.zeros(dim, dtype=torch.float32))
319
+
320
+ def forward(self, x: torch.Tensor):
321
+ return F.layer_norm(x.float(), (self.dim,), self.weight, self.bias, self.eps).type_as(x)
322
+
323
+
324
+ def precompute_freqs_cis(args: ModelArgs) -> torch.Tensor:
325
+ """
326
+ Precomputes frequency-based complex exponential values for rotary positional embeddings.
327
+
328
+ Args:
329
+ args (ModelArgs): Model arguments containing positional embedding parameters.
330
+
331
+ Returns:
332
+ torch.Tensor: Precomputed complex exponential values for positional embeddings.
333
+ """
334
+ dim = args.qk_rope_head_dim
335
+ seqlen = args.max_seq_len
336
+ beta_fast = args.beta_fast
337
+ beta_slow = args.beta_slow
338
+ base = args.rope_theta
339
+ factor = args.rope_factor
340
+
341
+ def find_correction_dim(num_rotations, dim, base, max_seq_len):
342
+ """
343
+ Computes the correction dimension for a given number of rotations in the rotary positional embedding.
344
+
345
+ Args:
346
+ num_rotations (float): Number of rotations to compute the correction for.
347
+ dim (int): Dimensionality of the embedding space.
348
+ base (float): Base value for the exponential computation.
349
+ max_seq_len (int): Maximum sequence length.
350
+
351
+ Returns:
352
+ float: The correction dimension based on the input parameters.
353
+ """
354
+ return dim * math.log(max_seq_len / (num_rotations * 2 * math.pi)) / (2 * math.log(base))
355
+
356
+ def find_correction_range(low_rot, high_rot, dim, base, max_seq_len):
357
+ """
358
+ Computes the range of correction dimensions for rotary positional embeddings.
359
+
360
+ Args:
361
+ low_rot (float): Lower bound for the number of rotations.
362
+ high_rot (float): Upper bound for the number of rotations.
363
+ dim (int): Dimensionality of the embedding space.
364
+ base (float): Base value for the exponential computation.
365
+ max_seq_len (int): Maximum sequence length.
366
+
367
+ Returns:
368
+ Tuple[int, int]: The range of correction dimensions (low, high), clamped to valid indices.
369
+ """
370
+ low = math.floor(find_correction_dim(low_rot, dim, base, max_seq_len))
371
+ high = math.ceil(find_correction_dim(high_rot, dim, base, max_seq_len))
372
+ return max(low, 0), min(high, dim-1)
373
+
374
+ def linear_ramp_factor(min, max, dim):
375
+ """
376
+ Computes a linear ramp function used to smooth values between a minimum and maximum range.
377
+
378
+ Args:
379
+ min (float): Minimum value for the ramp function.
380
+ max (float): Maximum value for the ramp function.
381
+ dim (int): Dimensionality of the ramp tensor.
382
+
383
+ Returns:
384
+ torch.Tensor: A tensor of shape (dim,) with values linearly interpolated between 0 and 1,
385
+ clamped to the range [0, 1].
386
+ """
387
+ if min == max:
388
+ max += 0.001
389
+ linear_func = (torch.arange(dim, dtype=torch.float32) - min) / (max - min)
390
+ ramp_func = torch.clamp(linear_func, 0, 1)
391
+ return ramp_func
392
+
393
+ freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim))
394
+ if seqlen > args.original_seq_len:
395
+ low, high = find_correction_range(beta_fast, beta_slow, dim, base, args.original_seq_len)
396
+ smooth = 1 - linear_ramp_factor(low, high, dim // 2)
397
+ freqs = freqs / factor * (1 - smooth) + freqs * smooth
398
+
399
+ t = torch.arange(seqlen)
400
+ freqs = torch.outer(t, freqs)
401
+ freqs_cis = torch.polar(torch.ones_like(freqs), freqs)
402
+ return freqs_cis
403
+
404
+
405
+ def apply_rotary_emb(x: torch.Tensor, freqs_cis: torch.Tensor, interleaved: bool = True) -> torch.Tensor:
406
+ """
407
+ Applies rotary positional embeddings to the input tensor.
408
+
409
+ Args:
410
+ x (torch.Tensor): Input tensor with positional embeddings to be applied.
411
+ freqs_cis (torch.Tensor): Precomputed complex exponential values for positional embeddings.
412
+
413
+ Returns:
414
+ torch.Tensor: Tensor with rotary embeddings applied.
415
+ """
416
+ dtype = x.dtype
417
+ shape = x.shape
418
+ if not interleaved:
419
+ x = x.view(*shape[:-1], 2, -1).transpose(-1, -2).contiguous()
420
+ x = torch.view_as_complex(x.float().view(*shape[:-1], -1, 2))
421
+ freqs_cis = freqs_cis.view(1, x.size(1), 1, x.size(-1))
422
+ y = torch.view_as_real(x * freqs_cis).flatten(3)
423
+ if not interleaved:
424
+ y = torch.cat([y[..., 0::2], y[..., 1::2]], dim=-1)
425
+ return y.to(dtype)
426
+
427
+
428
+ def rotate_activation(x: torch.Tensor) -> torch.Tensor:
429
+ assert x.dtype == torch.bfloat16
430
+ from fast_hadamard_transform import hadamard_transform
431
+ hidden_size = x.size(-1)
432
+ return hadamard_transform(x, scale=hidden_size ** -0.5)
433
+
434
+
435
+ class Indexer(torch.nn.Module):
436
+ def __init__(self, args: ModelArgs):
437
+ super().__init__()
438
+ self.dim: int = args.dim
439
+ self.n_heads: int = args.index_n_heads
440
+ self.n_local_heads = args.index_n_heads // world_size
441
+ self.head_dim: int = args.index_head_dim
442
+ self.rope_head_dim: int = args.qk_rope_head_dim
443
+ self.index_topk: int = args.index_topk
444
+ self.q_lora_rank: int = args.q_lora_rank
445
+ self.wq_b = Linear(self.q_lora_rank, self.n_heads * self.head_dim)
446
+ self.wk = Linear(self.dim, self.head_dim)
447
+ self.k_norm = LayerNorm(self.head_dim)
448
+ # weights_proj in the checkpoint is stored in bf16, while the parameters here are stored in fp32 for convenient.
449
+ self.weights_proj = Linear(self.dim, self.n_heads, dtype=torch.float32)
450
+ self.softmax_scale = self.head_dim ** -0.5
451
+ self.scale_fmt = args.scale_fmt
452
+
453
+ self.register_buffer("k_cache", torch.zeros(args.max_batch_size, args.max_seq_len, self.head_dim, dtype=torch.float8_e4m3fn), persistent=False)
454
+ self.register_buffer("k_scale_cache", torch.zeros(args.max_batch_size, args.max_seq_len, self.head_dim // block_size, dtype=torch.float32), persistent=False)
455
+
456
+
457
+ def forward(self, x: torch.Tensor, qr: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor]):
458
+ bsz, seqlen, _ = x.size()
459
+ end_pos = start_pos + seqlen
460
+ q = self.wq_b(qr)
461
+ q = q.view(bsz, seqlen, self.n_heads, self.head_dim)
462
+ q_pe, q_nope = torch.split(q, [self.rope_head_dim, self.head_dim - self.rope_head_dim], dim=-1)
463
+ # rope in indexer is not interleaved
464
+ q_pe = apply_rotary_emb(q_pe, freqs_cis, False)
465
+ q = torch.cat([q_pe, q_nope], dim=-1)
466
+ k = self.wk(x)
467
+ k = self.k_norm(k)
468
+ k_pe, k_nope = torch.split(k, [self.rope_head_dim, self.head_dim - self.rope_head_dim], dim=-1)
469
+ # rope in indexer is not interleaved
470
+ k_pe = apply_rotary_emb(k_pe.unsqueeze(2), freqs_cis, False).squeeze(2)
471
+ k = torch.cat([k_pe, k_nope], dim=-1)
472
+ q = rotate_activation(q)
473
+ k = rotate_activation(k)
474
+ q_fp8, q_scale = act_quant(q, block_size, self.scale_fmt)
475
+ k_fp8, k_scale = act_quant(k, block_size, self.scale_fmt)
476
+ self.k_cache[:bsz, start_pos:end_pos] = k_fp8
477
+ self.k_scale_cache[:bsz, start_pos:end_pos] = k_scale
478
+ weights = self.weights_proj(x.float()) * self.n_heads ** -0.5
479
+ weights = weights.unsqueeze(-1) * q_scale * self.softmax_scale
480
+ index_score = fp8_index(q_fp8.contiguous(), weights, self.k_cache[:bsz, :end_pos].contiguous(), self.k_scale_cache[:bsz, :end_pos].contiguous())
481
+ if mask is not None:
482
+ index_score += mask
483
+ topk_indices = index_score.topk(min(self.index_topk, end_pos), dim=-1)[1]
484
+ topk_indices_ = topk_indices.clone()
485
+ dist.broadcast(topk_indices_, src=0)
486
+ assert torch.all(topk_indices == topk_indices_), f"{topk_indices=} {topk_indices_=}"
487
+ return topk_indices
488
+
489
+
490
+ def weight_dequant(weight, scale):
491
+ shape = weight.shape
492
+ assert weight.dim() == 2
493
+ weight = weight.view(shape[0] // block_size, block_size, shape[1] // block_size, block_size).transpose(1, 2).contiguous().view(-1, block_size * block_size)
494
+ weight = (weight.float() * scale.view(-1, 1).float()).to(torch.get_default_dtype()).view(shape[0] // block_size, shape[1] // block_size, block_size, block_size).transpose(1, 2).contiguous().view(shape)
495
+ return weight
496
+
497
+
498
+ class MLA(nn.Module):
499
+ """
500
+ Multi-Head Latent Attention (MLA) Layer.
501
+
502
+ Attributes:
503
+ dim (int): Dimensionality of the input features.
504
+ n_heads (int): Number of attention heads.
505
+ n_local_heads (int): Number of local attention heads for distributed systems.
506
+ q_lora_rank (int): Rank for low-rank query projection.
507
+ kv_lora_rank (int): Rank for low-rank key/value projection.
508
+ qk_nope_head_dim (int): Dimensionality of non-positional query/key projections.
509
+ qk_rope_head_dim (int): Dimensionality of rotary-positional query/key projections.
510
+ qk_head_dim (int): Total dimensionality of query/key projections.
511
+ v_head_dim (int): Dimensionality of value projections.
512
+ softmax_scale (float): Scaling factor for softmax in attention computation.
513
+ """
514
+ def __init__(self, args: ModelArgs):
515
+ super().__init__()
516
+ self.dim = args.dim
517
+ self.n_heads = args.n_heads
518
+ self.n_local_heads = args.n_heads // world_size
519
+ self.q_lora_rank = args.q_lora_rank
520
+ self.kv_lora_rank = args.kv_lora_rank
521
+ self.qk_nope_head_dim = args.qk_nope_head_dim
522
+ self.qk_rope_head_dim = args.qk_rope_head_dim
523
+ self.qk_head_dim = args.qk_nope_head_dim + args.qk_rope_head_dim
524
+ self.v_head_dim = args.v_head_dim
525
+
526
+ self.wq_a = Linear(self.dim, self.q_lora_rank)
527
+ self.q_norm = RMSNorm(self.q_lora_rank)
528
+ self.wq_b = ColumnParallelLinear(self.q_lora_rank, self.n_heads * self.qk_head_dim)
529
+ self.wkv_a = Linear(self.dim, self.kv_lora_rank + self.qk_rope_head_dim)
530
+ self.kv_norm = RMSNorm(self.kv_lora_rank)
531
+ self.wkv_b = ColumnParallelLinear(self.kv_lora_rank, self.n_heads * (self.qk_nope_head_dim + self.v_head_dim))
532
+ self.wo = RowParallelLinear(self.n_heads * self.v_head_dim, self.dim)
533
+ self.softmax_scale = self.qk_head_dim ** -0.5
534
+ self.scale_fmt = args.scale_fmt
535
+ if args.max_seq_len > args.original_seq_len:
536
+ mscale = 0.1 * args.mscale * math.log(args.rope_factor) + 1.0
537
+ self.softmax_scale = self.softmax_scale * mscale * mscale
538
+
539
+ self.indexer = Indexer(args)
540
+
541
+ self.register_buffer("kv_cache", torch.zeros(args.max_batch_size, args.max_seq_len, self.kv_lora_rank), persistent=False)
542
+ self.register_buffer("pe_cache", torch.zeros(args.max_batch_size, args.max_seq_len, self.qk_rope_head_dim), persistent=False)
543
+ self.dequant_wkv_b = None
544
+
545
+ def forward(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor]):
546
+ """
547
+ Forward pass for the Multi-Head Latent Attention (MLA) Layer.
548
+
549
+ Args:
550
+ x (torch.Tensor): Input tensor of shape (batch_size, seq_len, dim).
551
+ start_pos (int): Starting position in the sequence for caching.
552
+ freqs_cis (torch.Tensor): Precomputed complex exponential values for rotary embeddings.
553
+ mask (Optional[torch.Tensor]): Mask tensor to exclude certain positions from attention.
554
+
555
+ Returns:
556
+ torch.Tensor: Output tensor with the same shape as the input.
557
+ """
558
+ bsz, seqlen, _ = x.size()
559
+ end_pos = start_pos + seqlen
560
+ qr = self.q_norm(self.wq_a(x))
561
+ q = self.wq_b(qr)
562
+ q = q.view(bsz, seqlen, self.n_local_heads, self.qk_head_dim)
563
+ q_nope, q_pe = torch.split(q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
564
+ q_pe = apply_rotary_emb(q_pe, freqs_cis)
565
+ kv = self.wkv_a(x)
566
+ kv, k_pe = torch.split(kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
567
+ kv = self.kv_norm(kv)
568
+ k_pe = apply_rotary_emb(k_pe.unsqueeze(2), freqs_cis)
569
+ # we use fp8 kv cache in actual deployment, so here we simulate the precision by casting kv to fp8 and then back to bf16.
570
+ kv_fp8, kv_scale = act_quant(kv, block_size, self.scale_fmt)
571
+ kv = (kv_fp8.view(-1, block_size).float() * kv_scale.view(-1, 1)).to(kv.dtype).view_as(kv)
572
+ self.kv_cache[:bsz, start_pos:end_pos] = kv
573
+ self.pe_cache[:bsz, start_pos:end_pos] = k_pe.squeeze(2)
574
+ if mask is not None: # MHA prefill
575
+ q = torch.cat([q_nope, q_pe], dim=-1)
576
+ kv = self.wkv_b(kv)
577
+ kv = kv.view(bsz, seqlen, self.n_local_heads, self.qk_nope_head_dim + self.v_head_dim)
578
+ k_nope, v = torch.split(kv, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
579
+ k = torch.cat([k_nope, k_pe.expand(-1, -1, self.n_local_heads, -1)], dim=-1)
580
+ scores = torch.einsum("bshd,bthd->bsht", q, k).mul_(self.softmax_scale)
581
+
582
+ # indexer
583
+ topk_indices = self.indexer(x, qr, start_pos, freqs_cis, mask)
584
+ index_mask = torch.full((bsz, seqlen, seqlen), float("-inf"), device=x.device).scatter_(-1, topk_indices, 0)
585
+ index_mask += mask
586
+ scores += index_mask.unsqueeze(2)
587
+
588
+ scores = scores.softmax(dim=-1)
589
+ x = torch.einsum("bsht,bthd->bshd", scores, v)
590
+ else: # MQA decode
591
+ if self.dequant_wkv_b is None and self.wkv_b.scale is not None:
592
+ self.dequant_wkv_b = weight_dequant(self.wkv_b.weight, self.wkv_b.scale)
593
+ wkv_b = self.wkv_b.weight if self.dequant_wkv_b is None else self.dequant_wkv_b
594
+ wkv_b = wkv_b.view(self.n_local_heads, -1, self.kv_lora_rank)
595
+ q_nope = torch.einsum("bshd,hdc->bshc", q_nope, wkv_b[:, :self.qk_nope_head_dim])
596
+ scores = (torch.einsum("bshc,btc->bsht", q_nope, self.kv_cache[:bsz, :end_pos]) +
597
+ torch.einsum("bshr,btr->bsht", q_pe, self.pe_cache[:bsz, :end_pos])) * self.softmax_scale
598
+
599
+ # indexer
600
+ topk_indices = self.indexer(x, qr, start_pos, freqs_cis, mask)
601
+ index_mask = torch.full((bsz, 1, end_pos), float("-inf"), device=x.device).scatter_(-1, topk_indices, 0)
602
+ scores += index_mask.unsqueeze(2)
603
+
604
+ scores = scores.softmax(dim=-1)
605
+ x = torch.einsum("bsht,btc->bshc", scores, self.kv_cache[:bsz, :end_pos])
606
+ x = torch.einsum("bshc,hdc->bshd", x, wkv_b[:, -self.v_head_dim:])
607
+ x = self.wo(x.flatten(2))
608
+ return x
609
+
610
+
611
+ class MLP(nn.Module):
612
+ """
613
+ Multi-Layer Perceptron (MLP) used as a feed-forward layer.
614
+
615
+ Attributes:
616
+ w1 (nn.Module): Linear layer for input-to-hidden transformation.
617
+ w2 (nn.Module): Linear layer for hidden-to-output transformation.
618
+ w3 (nn.Module): Additional linear layer for feature transformation.
619
+ """
620
+ def __init__(self, dim: int, inter_dim: int, reduce_output: bool = True):
621
+ """
622
+ Initializes the MLP layer.
623
+
624
+ Args:
625
+ dim (int): Input and output dimensionality.
626
+ inter_dim (int): Hidden layer dimensionality.
627
+ """
628
+ super().__init__()
629
+ self.w1 = ColumnParallelLinear(dim, inter_dim)
630
+ self.w2 = RowParallelLinear(inter_dim, dim, reduce_output=reduce_output)
631
+ self.w3 = ColumnParallelLinear(dim, inter_dim)
632
+
633
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
634
+ """
635
+ Forward pass for the MLP layer.
636
+
637
+ Args:
638
+ x (torch.Tensor): Input tensor.
639
+
640
+ Returns:
641
+ torch.Tensor: Output tensor after MLP computation.
642
+ """
643
+ return self.w2((F.silu(self.w1(x).float()) * self.w3(x).float()).type_as(x))
644
+
645
+
646
+ class Gate(nn.Module):
647
+ """
648
+ Gating mechanism for routing inputs in a mixture-of-experts (MoE) model.
649
+
650
+ Attributes:
651
+ dim (int): Dimensionality of input features.
652
+ topk (int): Number of top experts activated for each input.
653
+ n_groups (int): Number of groups for routing.
654
+ topk_groups (int): Number of groups to route inputs to.
655
+ score_func (str): Scoring function ('softmax' or 'sigmoid').
656
+ route_scale (float): Scaling factor for routing weights.
657
+ weight (torch.nn.Parameter): Learnable weights for the gate.
658
+ bias (Optional[torch.nn.Parameter]): Optional bias term for the gate.
659
+ """
660
+ def __init__(self, args: ModelArgs):
661
+ """
662
+ Initializes the Gate module.
663
+
664
+ Args:
665
+ args (ModelArgs): Model arguments containing gating parameters.
666
+ """
667
+ super().__init__()
668
+ self.dim = args.dim
669
+ self.topk = args.n_activated_experts
670
+ self.n_groups = args.n_expert_groups
671
+ self.topk_groups = args.n_limited_groups
672
+ self.score_func = args.score_func
673
+ self.route_scale = args.route_scale
674
+ self.weight = nn.Parameter(torch.empty(args.n_routed_experts, args.dim))
675
+ self.bias = nn.Parameter(torch.empty(args.n_routed_experts, dtype=torch.float32)) if self.dim == 7168 else None
676
+
677
+ def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
678
+ """
679
+ Forward pass for the gating mechanism.
680
+
681
+ Args:
682
+ x (torch.Tensor): Input tensor.
683
+
684
+ Returns:
685
+ Tuple[torch.Tensor, torch.Tensor]: Routing weights and selected expert indices.
686
+ """
687
+ scores = linear(x.float(), self.weight.float())
688
+ if self.score_func == "softmax":
689
+ scores = scores.softmax(dim=-1)
690
+ else:
691
+ scores = scores.sigmoid()
692
+ original_scores = scores
693
+ if self.bias is not None:
694
+ scores = scores + self.bias
695
+ if self.n_groups > 1:
696
+ scores = scores.view(x.size(0), self.n_groups, -1)
697
+ if self.bias is None:
698
+ group_scores = scores.amax(dim=-1)
699
+ else:
700
+ group_scores = scores.topk(2, dim=-1)[0].sum(dim=-1)
701
+ indices = group_scores.topk(self.topk_groups, dim=-1)[1]
702
+ mask = scores.new_ones(x.size(0), self.n_groups, dtype=bool).scatter_(1, indices, False)
703
+ scores = scores.masked_fill_(mask.unsqueeze(-1), float("-inf")).flatten(1)
704
+ indices = scores.topk(self.topk, dim=-1)[1]
705
+ weights = original_scores.gather(1, indices)
706
+ if self.score_func == "sigmoid":
707
+ weights /= weights.sum(dim=-1, keepdim=True)
708
+ weights *= self.route_scale
709
+ return weights, indices
710
+
711
+
712
+ class Expert(nn.Module):
713
+ """
714
+ Expert layer for Mixture-of-Experts (MoE) models.
715
+
716
+ Attributes:
717
+ w1 (nn.Module): Linear layer for input-to-hidden transformation.
718
+ w2 (nn.Module): Linear layer for hidden-to-output transformation.
719
+ w3 (nn.Module): Additional linear layer for feature transformation.
720
+ """
721
+ def __init__(self, dim: int, inter_dim: int):
722
+ """
723
+ Initializes the Expert layer.
724
+
725
+ Args:
726
+ dim (int): Input and output dimensionality.
727
+ inter_dim (int): Hidden layer dimensionality.
728
+ """
729
+ super().__init__()
730
+ self.w1 = Linear(dim, inter_dim)
731
+ self.w2 = Linear(inter_dim, dim)
732
+ self.w3 = Linear(dim, inter_dim)
733
+
734
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
735
+ """
736
+ Forward pass for the Expert layer.
737
+
738
+ Args:
739
+ x (torch.Tensor): Input tensor.
740
+
741
+ Returns:
742
+ torch.Tensor: Output tensor after expert computation.
743
+ """
744
+ return self.w2((F.silu(self.w1(x).float()) * self.w3(x).float()).type_as(x))
745
+
746
+
747
+ class MoE(nn.Module):
748
+ """
749
+ Mixture-of-Experts (MoE) module.
750
+
751
+ Attributes:
752
+ dim (int): Dimensionality of input features.
753
+ n_routed_experts (int): Total number of experts in the model.
754
+ n_local_experts (int): Number of experts handled locally in distributed systems.
755
+ n_activated_experts (int): Number of experts activated for each input.
756
+ gate (nn.Module): Gating mechanism to route inputs to experts.
757
+ experts (nn.ModuleList): List of expert modules.
758
+ shared_experts (nn.Module): Shared experts applied to all inputs.
759
+ """
760
+ def __init__(self, args: ModelArgs):
761
+ """
762
+ Initializes the MoE module.
763
+
764
+ Args:
765
+ args (ModelArgs): Model arguments containing MoE parameters.
766
+ """
767
+ super().__init__()
768
+ self.dim = args.dim
769
+ assert args.n_routed_experts % world_size == 0, f"Number of experts must be divisible by world size (world_size={world_size})"
770
+ self.n_routed_experts = args.n_routed_experts
771
+ self.n_local_experts = args.n_routed_experts // world_size
772
+ self.n_activated_experts = args.n_activated_experts
773
+ self.experts_start_idx = rank * self.n_local_experts
774
+ self.experts_end_idx = self.experts_start_idx + self.n_local_experts
775
+ self.gate = Gate(args)
776
+ self.experts = nn.ModuleList([Expert(args.dim, args.moe_inter_dim) if self.experts_start_idx <= i < self.experts_end_idx else None
777
+ for i in range(self.n_routed_experts)])
778
+ self.shared_experts = MLP(args.dim, args.n_shared_experts * args.moe_inter_dim, reduce_output=False)
779
+
780
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
781
+ """
782
+ Forward pass for the MoE module.
783
+
784
+ Args:
785
+ x (torch.Tensor): Input tensor.
786
+
787
+ Returns:
788
+ torch.Tensor: Output tensor after expert routing and computation.
789
+ """
790
+ shape = x.size()
791
+ x = x.view(-1, self.dim)
792
+ weights, indices = self.gate(x)
793
+ y = torch.zeros_like(x, dtype=torch.float32)
794
+ counts = torch.bincount(indices.flatten(), minlength=self.n_routed_experts).tolist()
795
+ for i in range(self.experts_start_idx, self.experts_end_idx):
796
+ if counts[i] == 0:
797
+ continue
798
+ expert = self.experts[i]
799
+ idx, top = torch.where(indices == i)
800
+ y[idx] += expert(x[idx]) * weights[idx, top, None]
801
+ y += self.shared_experts(x)
802
+ if world_size > 1:
803
+ dist.all_reduce(y)
804
+ return y.type_as(x).view(shape)
805
+
806
+
807
+ class Block(nn.Module):
808
+ """
809
+ Transformer block combining attention and feed-forward layers.
810
+
811
+ Attributes:
812
+ attn (nn.Module): Attention layer (MLA).
813
+ ffn (nn.Module): Feed-forward network (MLP or MoE).
814
+ attn_norm (nn.Module): Layer normalization for attention.
815
+ ffn_norm (nn.Module): Layer normalization for feed-forward network.
816
+ """
817
+ def __init__(self, layer_id: int, args: ModelArgs):
818
+ """
819
+ Initializes the Transformer block.
820
+
821
+ Args:
822
+ layer_id (int): Layer index in the transformer.
823
+ args (ModelArgs): Model arguments containing block parameters.
824
+ """
825
+ super().__init__()
826
+ self.attn = MLA(args)
827
+ self.ffn = MLP(args.dim, args.inter_dim) if layer_id < args.n_dense_layers else MoE(args)
828
+ self.attn_norm = RMSNorm(args.dim)
829
+ self.ffn_norm = RMSNorm(args.dim)
830
+
831
+ def forward(self, x: torch.Tensor, residual: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor]) -> torch.Tensor:
832
+ """
833
+ Forward pass for the Transformer block.
834
+
835
+ Args:
836
+ x (torch.Tensor): Input tensor.
837
+ start_pos (int): Starting position in the sequence.
838
+ freqs_cis (torch.Tensor): Precomputed complex exponential values for rotary embeddings.
839
+ mask (Optional[torch.Tensor]): Mask tensor to exclude certain positions from attention.
840
+
841
+ Returns:
842
+ torch.Tensor: Output tensor after block computation.
843
+ """
844
+ if residual is None:
845
+ x, residual = self.attn_norm(x), x
846
+ else:
847
+ x, residual = self.attn_norm(x, residual)
848
+ x = self.attn(x, start_pos, freqs_cis, mask)
849
+ x, residual = self.ffn_norm(x, residual)
850
+ x = self.ffn(x)
851
+ return x, residual
852
+
853
+
854
+ class Transformer(nn.Module):
855
+ """
856
+ Transformer model with positional embeddings, multiple layers, and output projection.
857
+
858
+ Attributes:
859
+ max_seq_len (int): Maximum sequence length for the transformer.
860
+ embed (nn.Module): Embedding layer for input tokens.
861
+ layers (torch.nn.ModuleList): List of transformer blocks.
862
+ norm (nn.Module): Layer normalization applied after all blocks.
863
+ head (nn.Module): Output projection layer mapping to vocabulary size.
864
+ freqs_cis (torch.Tensor): Precomputed complex exponential values for rotary embeddings.
865
+ """
866
+ def __init__(self, args: ModelArgs):
867
+ """
868
+ Initializes the Transformer model.
869
+
870
+ Args:
871
+ args (ModelArgs): Model arguments containing transformer parameters.
872
+ """
873
+ global world_size, rank
874
+ world_size = dist.get_world_size() if dist.is_initialized() else 1
875
+ rank = dist.get_rank() if dist.is_initialized() else 0
876
+ Linear.dtype = torch.float8_e4m3fn if args.dtype == "fp8" else torch.bfloat16
877
+ Linear.scale_fmt = args.scale_fmt
878
+ super().__init__()
879
+ self.max_seq_len = args.max_seq_len
880
+ self.embed = ParallelEmbedding(args.vocab_size, args.dim)
881
+ self.layers = torch.nn.ModuleList()
882
+ for layer_id in range(args.n_layers):
883
+ self.layers.append(Block(layer_id, args))
884
+ self.norm = RMSNorm(args.dim)
885
+ # lm_head in the checkpoint is stored in bf16, while the parameter here is stored in fp32 for easier computation of logits later.
886
+ self.head = ColumnParallelLinear(args.dim, args.vocab_size, dtype=torch.float32)
887
+ self.register_buffer("freqs_cis", precompute_freqs_cis(args), persistent=False)
888
+
889
+ @torch.inference_mode()
890
+ def forward(self, tokens: torch.Tensor, start_pos: int = 0):
891
+ """
892
+ Forward pass for the Transformer model.
893
+
894
+ Args:
895
+ tokens (torch.Tensor): Input tensor of token IDs with shape (batch_size, seq_len).
896
+ start_pos (int, optional): Starting position in the sequence for rotary embeddings. Defaults to 0.
897
+
898
+ Returns:
899
+ torch.Tensor: Logits tensor of shape (batch_size, vocab_size).
900
+ """
901
+ seqlen = tokens.size(1)
902
+ freqs_cis = self.freqs_cis[start_pos:start_pos+seqlen]
903
+ mask = torch.full((seqlen, seqlen), float("-inf"), device=tokens.device).triu_(1) if seqlen > 1 else None
904
+ h, residual = self.embed(tokens), None
905
+ for layer in self.layers:
906
+ h, residual = layer(h, residual, start_pos, freqs_cis, mask)
907
+ h, _ = self.norm(h, residual)
908
+ logits = self.head(h[:, -1].float())
909
+ if world_size > 1:
910
+ all_logits = [torch.empty_like(logits) for _ in range(world_size)]
911
+ dist.all_gather(all_logits, logits)
912
+ logits = torch.cat(all_logits, dim=-1)
913
+ return logits
914
+
915
+
916
+ if __name__ == "__main__":
917
+ torch.set_default_dtype(torch.bfloat16)
918
+ torch.set_default_device("cuda")
919
+ torch.manual_seed(0)
920
+ args = ModelArgs()
921
+ x = torch.randint(0, args.vocab_size, (2, 128))
922
+ model = Transformer(args)
923
+ print(model(x).size())
inference/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch
2
+ transformers
3
+ safetensors
4
+ fast_hadamard_transform
5
+ tilelang==0.1.6
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4fcfa230a056cd38141645b3ac3cff784d610bf63f8ee98528d38dc4bf0760ae
3
+ size 8305696
modeling_deepseek_v32.py ADDED
@@ -0,0 +1,935 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 the HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ import math
17
+ from collections.abc import Callable
18
+ from typing import Optional
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+
24
+ from transformers.activations import ACT2FN
25
+ from transformers.cache_utils import Cache, DynamicCache
26
+ from transformers.generation import GenerationMixin
27
+ from transformers.masking_utils import create_causal_mask
28
+ from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
29
+ from transformers.modeling_layers import GradientCheckpointingLayer
30
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
31
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
32
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
33
+ from transformers.processing_utils import Unpack
34
+ from transformers.utils import TransformersKwargs, can_return_tuple, logging
35
+ from transformers.utils.generic import check_model_inputs
36
+ from .configuration_deepseek_v32 import DeepseekV32Config
37
+
38
+
39
+ logger = logging.get_logger(__name__)
40
+
41
+
42
+ class DeepseekV32RMSNorm(nn.Module):
43
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
44
+ """
45
+ DeepseekV32RMSNorm is equivalent to T5LayerNorm
46
+ """
47
+ super().__init__()
48
+ self.weight = nn.Parameter(torch.ones(hidden_size))
49
+ self.variance_epsilon = eps
50
+
51
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
52
+ input_dtype = hidden_states.dtype
53
+ hidden_states = hidden_states.to(torch.float32)
54
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
55
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
56
+ return self.weight * hidden_states.to(input_dtype)
57
+
58
+ def extra_repr(self):
59
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
60
+
61
+
62
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
63
+ """Applies Rotary Position Embedding to the query and key tensors.
64
+
65
+ Args:
66
+ q (`torch.Tensor`): The query tensor.
67
+ k (`torch.Tensor`): The key tensor.
68
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
69
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
70
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
71
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
72
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
73
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
74
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
75
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
76
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
77
+ Returns:
78
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
79
+ """
80
+ cos = cos.unsqueeze(unsqueeze_dim)
81
+ sin = sin.unsqueeze(unsqueeze_dim)
82
+ q_embed = (q * cos) + (rotate_half(q) * sin)
83
+ k_embed = (k * cos) + (rotate_half(k) * sin)
84
+ return q_embed, k_embed
85
+
86
+
87
+ def rotate_half(x):
88
+ """Rotates half the hidden dims of the input."""
89
+ x1 = x[..., : x.shape[-1] // 2]
90
+ x2 = x[..., x.shape[-1] // 2 :]
91
+ return torch.cat((-x2, x1), dim=-1)
92
+
93
+
94
+ def apply_rotary_pos_emb_interleave(q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
95
+ r"""
96
+ TODO let's just use the original freqcis computation to not have the view
97
+ transpose + reshape! This is not optimized!
98
+ Applies Rotary Position Embedding to the query and key tensors.
99
+
100
+ Args:
101
+ q (`torch.Tensor`): The query tensor.
102
+ k (`torch.Tensor`): The key tensor.
103
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
104
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
105
+ position_ids (`torch.Tensor`):
106
+ The position indices of the tokens corresponding to the query and key tensors. For example, this can be
107
+ used to pass offsetted position ids when working with a KV-cache.
108
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
109
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
110
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
111
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
112
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
113
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
114
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
115
+ Returns:
116
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
117
+ """
118
+ cos = cos.unsqueeze(unsqueeze_dim)
119
+ sin = sin.unsqueeze(unsqueeze_dim)
120
+
121
+ b, h, s, d = q.shape
122
+ q = q.view(b, h, s, d // 2, 2).transpose(4, 3).reshape(b, h, s, d)
123
+
124
+ b, h, s, d = k.shape
125
+ k = k.view(b, h, s, d // 2, 2).transpose(4, 3).reshape(b, h, s, d)
126
+
127
+ q_embed = (q * cos) + (rotate_half(q) * sin)
128
+ k_embed = (k * cos) + (rotate_half(k) * sin)
129
+ return q_embed, k_embed
130
+
131
+
132
+ def yarn_get_mscale(scale=1, mscale=1):
133
+ if scale <= 1:
134
+ return 1.0
135
+ return 0.1 * mscale * math.log(scale) + 1.0
136
+
137
+
138
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
139
+ """
140
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
141
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
142
+ """
143
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
144
+ if n_rep == 1:
145
+ return hidden_states
146
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
147
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
148
+
149
+
150
+ def eager_attention_forward(
151
+ module: nn.Module,
152
+ query: torch.Tensor,
153
+ key: torch.Tensor,
154
+ value: torch.Tensor,
155
+ attention_mask: torch.Tensor | None,
156
+ scaling: float,
157
+ dropout: float = 0.0,
158
+ **kwargs: Unpack[TransformersKwargs],
159
+ ):
160
+ key_states = repeat_kv(key, module.num_key_value_groups)
161
+ value_states = repeat_kv(value, module.num_key_value_groups)
162
+
163
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
164
+ if attention_mask is not None:
165
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
166
+ attn_weights = attn_weights + causal_mask
167
+
168
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
169
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
170
+ attn_output = torch.matmul(attn_weights, value_states)
171
+ attn_output = attn_output.transpose(1, 2).contiguous()
172
+
173
+ return attn_output, attn_weights
174
+
175
+
176
+ class DeepseekV32Indexer(nn.Module):
177
+ def __init__(self, config: "DeepseekV32Config", index_layer_idx: int):
178
+ super().__init__()
179
+ self.config = config
180
+ self.layer_idx = index_layer_idx
181
+
182
+ self.hidden_size: int = config.hidden_size
183
+ self.num_heads: int = config.index_n_heads
184
+ self.num_local_heads: int = config.index_n_heads # world_size handling can be added as needed
185
+ self.head_dim: int = config.index_head_dim
186
+ self.qk_rope_head_dim: int = config.qk_rope_head_dim
187
+ self.index_topk: int = config.index_topk
188
+ self.q_lora_rank: int = config.q_lora_rank
189
+
190
+ self.wq_b = nn.Linear(self.q_lora_rank, self.num_heads * self.head_dim, bias=False)
191
+ self.wk = nn.Linear(self.hidden_size, self.head_dim, bias=False)
192
+ self.k_norm = nn.LayerNorm(self.head_dim)
193
+ self.weights_proj = nn.Linear(self.hidden_size, self.num_heads, dtype=torch.get_default_dtype(), bias=False)
194
+ self.softmax_scale = self.head_dim**-0.5
195
+
196
+ @torch.no_grad()
197
+ def forward(
198
+ self,
199
+ hidden_states: torch.Tensor, # [B, S, hidden]
200
+ q_resid: torch.Tensor, # [B, S, q_lora_rank]
201
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
202
+ attention_mask: torch.Tensor | None,
203
+ past_key_values_index: "Cache",
204
+ cache_position: torch.LongTensor | None,
205
+ ) -> torch.LongTensor:
206
+ B, S, _ = hidden_states.shape
207
+ cos, sin = position_embeddings
208
+
209
+ # Queries
210
+ q_states = self.wq_bj(q_resid) # [B, S, H*D]
211
+ q_states = q_states.view(B, S, self.num_heads, self.head_dim) # [B, S, H, D]
212
+ q_rot, q_pass = torch.split(q_states, [self.qk_rope_head_dim, self.head_dim - self.qk_rope_head_dim], dim=-1)
213
+ q_rot = apply_rotary_pos_emb_interleave(q_rot, cos, sin) # [B, S, H, rope_D]
214
+ q_states = torch.cat([q_rot, q_pass], dim=-1) # [B, S, H, D]
215
+
216
+ # Keys
217
+ k = self.k_norm(self.wk(hidden_states)) # [B, S, D]
218
+ k_rot, k_pass = torch.split(k, [self.qk_rope_head_dim, self.head_dim - self.qk_rope_head_dim], dim=-1)
219
+ # MLA uses single-head rope stream, then expands later; keep [B, 1, S, rope_D] here
220
+ k_rot = k_rot.unsqueeze(1) # [B, 1, S, rope_D]
221
+ k_rot = apply_rotary_pos_emb_interleave(k_rot, cos, sin) # [B, 1, S, rope_D]
222
+ k_states = torch.cat(
223
+ [
224
+ k_rot.expand(B, self.num_heads, S, -1), # expand rope
225
+ k_pass.view(B, 1, S, -1).expand(B, self.num_heads, S, -1),
226
+ ],
227
+ dim=-1,
228
+ ) # [B, H, S, D]
229
+
230
+ # Quantize (per provided utilities)
231
+ # Update indexer cache (layer idx belongs to the attention layer using this indexer)
232
+ # We store as: keys = k_fp8 (as [B, 1, S, D] or [B, H, S, D]? We keep [B, 1, S, D] like original)
233
+ # For compactness, collapse heads to 1 for the indexer (you can keep H if your fp8_index expects it).
234
+ k_1h = k_states.mean(dim=1, keepdim=True) # [B, 1, S, D] (cheap head merge; adjust if needed)
235
+ k_cache = past_key_values_index.update(k_1h, self.layer_idx, cache_kwargs={"cache_position": cache_position})
236
+
237
+ # Weights per head
238
+ head_weights = self.weights_proj(hidden_states) * (self.num_heads**-0.5) # [B, S, H]
239
+ head_weights = head_weights.unsqueeze(-1) * self.softmax_scale # [B, S, H, *]
240
+ logits = torch.matmul(k_cache.unsqueeze(1), q_states.transpose(-1, -2)) # [B, M, N, H]
241
+
242
+ # ReLU and sum over heads -> [B, M, N]
243
+ logits.clamp_min_(0)
244
+ index_scores = logits.sum(dim=-1) # [B, M, N]
245
+
246
+ if attention_mask is not None:
247
+ index_scores = index_scores + attention_mask
248
+
249
+ T = index_scores.shape[-1]
250
+ topk = min(self.index_topk, T)
251
+ topk_indices = index_scores.topk(topk, dim=-1).indices # [..., topk]
252
+ return topk_indices
253
+
254
+ class DeepseekV32Attention(nn.Module):
255
+ """
256
+ DeepSeek V3.2 sparse attention mechanism with indexer.
257
+
258
+ This implements the native sparse attention from [DeepSeek V3.2](https://huggingface.co/deepseek-ai/DeepSeek-V3.2) which uses
259
+ an indexer to select top-k tokens for attention computation, making it more efficient for long sequences.
260
+ """
261
+
262
+ def __init__(self, config: DeepseekV32Config, layer_idx: int):
263
+ super().__init__()
264
+ self.config = config
265
+ self.layer_idx = layer_idx
266
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
267
+ self.attention_dropout = config.attention_dropout
268
+ self.num_heads = config.num_attention_heads
269
+
270
+ self.q_lora_rank = config.q_lora_rank
271
+ self.qk_rope_head_dim = config.qk_rope_head_dim
272
+ self.kv_lora_rank = config.kv_lora_rank
273
+ self.v_head_dim = config.v_head_dim
274
+ self.qk_nope_head_dim = config.qk_nope_head_dim
275
+ self.qk_head_dim = config.qk_head_dim
276
+ self.index_topk = config.index_topk
277
+
278
+ self.is_causal = True
279
+
280
+ # Query projection
281
+ if self.q_lora_rank is None:
282
+ self.q_proj = nn.Linear(config.hidden_size, self.num_heads * self.qk_head_dim, bias=False)
283
+ else:
284
+ self.q_a_proj = nn.Linear(config.hidden_size, config.q_lora_rank, bias=config.attention_bias)
285
+ self.q_a_layernorm = DeepseekV32RMSNorm(config.q_lora_rank)
286
+ self.q_b_proj = nn.Linear(config.q_lora_rank, self.num_heads * self.qk_head_dim, bias=False)
287
+
288
+ # Key-Value projections
289
+ self.kv_a_proj_with_mqa = nn.Linear(
290
+ config.hidden_size,
291
+ self.kv_lora_rank + self.qk_rope_head_dim,
292
+ bias=config.attention_bias,
293
+ )
294
+ self.kv_a_layernorm = DeepseekV32RMSNorm(self.kv_lora_rank)
295
+ self.kv_b_proj = nn.Linear(
296
+ self.kv_lora_rank,
297
+ self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
298
+ bias=False,
299
+ )
300
+
301
+ # Output projection
302
+ self.o_proj = nn.Linear(
303
+ self.num_heads * self.v_head_dim,
304
+ config.hidden_size,
305
+ bias=config.attention_bias,
306
+ )
307
+
308
+ # Indexer components for sparse attention
309
+ self.wq_b = nn.Linear(config.q_lora_rank, self.num_heads * self.qk_head_dim, bias=False)
310
+ self.wk = nn.Linear(config.hidden_size, self.qk_head_dim, bias=config.attention_bias)
311
+ self.k_norm = DeepseekV32RMSNorm(self.qk_head_dim)
312
+ self.weights_proj = nn.Linear(config.hidden_size, self.num_heads, bias=False)
313
+
314
+ self.scaling = self.qk_head_dim ** (-0.5)
315
+ if self.config.rope_scaling.get("rope_type", "default") != "default":
316
+ mscale_all_dim = self.config.rope_scaling.get("mscale_all_dim", 0)
317
+ scaling_factor = self.config.rope_scaling["factor"]
318
+ if mscale_all_dim:
319
+ mscale = yarn_get_mscale(scaling_factor, mscale_all_dim)
320
+ self.scaling = self.scaling * mscale * mscale
321
+
322
+ self.indexer = DeepseekV32Indexer(config, layer_idx)
323
+
324
+ def forward(
325
+ self,
326
+ hidden_states: torch.Tensor,
327
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
328
+ attention_mask: torch.Tensor | None,
329
+ past_key_values: Cache | None = None,
330
+ cache_position: torch.LongTensor | None = None,
331
+ **kwargs: Unpack[FlashAttentionKwargs],
332
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
333
+ batch_size, seq_length = hidden_states.shape[:-1]
334
+
335
+ # For training or when index_topk is not effective, fall back to standard attention
336
+ # This is a simplified implementation - in practice, you'd implement the full sparse indexer
337
+ if self.training or seq_length <= self.index_topk:
338
+ logger.warning_once(
339
+ "DeepSeek V3.2 sparse attention is not fully implemented in this version. "
340
+ "Falling back to standard attention. For production use, please use vLLM or "
341
+ "other optimized inference engines.",
342
+ )
343
+ return self._standard_attention(
344
+ hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs
345
+ )
346
+
347
+ # Sparse attention implementation would go here
348
+ # This requires custom CUDA kernels for efficient top-k selection and indexing
349
+ return self._dsa_attention(
350
+ hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs
351
+ )
352
+
353
+ def _standard_attention(
354
+ self,
355
+ hidden_states: torch.Tensor,
356
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
357
+ attention_mask: torch.Tensor | None,
358
+ past_key_values: Cache | None = None,
359
+ cache_position: torch.LongTensor | None = None,
360
+ **kwargs: Unpack[FlashAttentionKwargs],
361
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
362
+ """Standard attention fallback (same as DeepSeek V3)"""
363
+ batch_size, seq_length = hidden_states.shape[:-1]
364
+ query_shape = (batch_size, seq_length, -1, self.qk_head_dim)
365
+ key_shape = (batch_size, seq_length, -1, self.qk_nope_head_dim + self.v_head_dim)
366
+
367
+ if self.q_lora_rank is None:
368
+ q_states = self.q_proj(hidden_states)
369
+ else:
370
+ q_states = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
371
+ q_states = q_states.view(query_shape).transpose(1, 2)
372
+ q_pass, q_rot = torch.split(q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
373
+
374
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
375
+ k_pass, k_rot = torch.split(compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
376
+
377
+ k_pass = self.kv_b_proj(self.kv_a_layernorm(k_pass)).view(key_shape).transpose(1, 2)
378
+ k_pass, value_states = torch.split(k_pass, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
379
+
380
+ k_rot = k_rot.view(batch_size, 1, seq_length, self.qk_rope_head_dim)
381
+
382
+ cos, sin = position_embeddings
383
+ if self.config.rope_interleave:
384
+ q_rot, k_rot = apply_rotary_pos_emb_interleave(q_rot, k_rot, cos, sin)
385
+ else:
386
+ q_rot, k_rot = apply_rotary_pos_emb(q_rot, k_rot, cos, sin)
387
+ k_rot = k_rot.expand(*k_pass.shape[:-1], -1)
388
+
389
+ query_states = torch.cat((q_pass, q_rot), dim=-1)
390
+ key_states = torch.cat((k_pass, k_rot), dim=-1)
391
+
392
+ if past_key_values is not None:
393
+ cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
394
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_kwargs)
395
+
396
+ if self.config._attn_implementation == "flash_attention_2" and self.qk_head_dim != self.v_head_dim:
397
+ value_states = F.pad(value_states, [0, self.qk_head_dim - self.v_head_dim])
398
+
399
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
400
+ self.config._attn_implementation, eager_attention_forward
401
+ )
402
+
403
+ attn_output, attn_weights = attention_interface(
404
+ self,
405
+ query_states,
406
+ key_states,
407
+ value_states,
408
+ attention_mask,
409
+ dropout=0.0 if not self.training else self.attention_dropout,
410
+ scaling=self.scaling,
411
+ **kwargs,
412
+ )
413
+
414
+ if self.config._attn_implementation == "flash_attention_2" and self.qk_head_dim != self.v_head_dim:
415
+ attn_output = attn_output[:, :, :, : self.v_head_dim]
416
+
417
+ attn_output = attn_output.reshape(batch_size, seq_length, -1).contiguous()
418
+ attn_output = self.o_proj(attn_output)
419
+ return attn_output, attn_weights
420
+
421
+ def _dsa_attention(
422
+ self,
423
+ hidden_states: torch.Tensor,
424
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
425
+ attention_mask: torch.Tensor | None,
426
+ past_key_values: Cache | None = None,
427
+ cache_position: torch.LongTensor | None = None,
428
+ **kwargs: Unpack[FlashAttentionKwargs]
429
+ ):
430
+
431
+ B, S, _ = hidden_states.shape
432
+ cos, sin = position_embeddings
433
+
434
+ # ----- Q path -----
435
+ q_resid = self.q_a_layernorm(self.q_a_proj(hidden_states)) # [B, S, q_lora_rank]
436
+ q_states = self.q_b_proj(q_resid).view(B, S, self.num_heads, self.qk_head_dim) # [B, S, H, D]
437
+ # Split into pass/rot then apply RoPE on q_rot
438
+ q_pass, q_rot = torch.split(q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
439
+ q_rot = apply_rotary_pos_emb(q_rot, cos, sin) # [B, S, H, rope_D]
440
+ q_states = torch.cat([q_pass, q_rot], dim=-1) # [B, S, H, D]
441
+
442
+ # Layout for matmul: [B, H, S, D]
443
+ q_states = q_states.transpose(1, 2).contiguous() # [B, H, S, D]
444
+
445
+ # ----- KV path (compressed + rope stream) -----
446
+ kv_all = self.kv_a_proj_with_mqa(hidden_states) # [B, S, kv_rank + rope_D]
447
+ kv_compressed, k_rot = torch.split(kv_all, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
448
+ kv_compressed = self.kv_a_layernorm(kv_compressed) # [B, S, kv_rank]
449
+ # Pre-project to K_pass and V
450
+ kv_proj = self.kv_b_proj(kv_compressed) # [B, S, H*(qk_nope + v)]
451
+ kv_proj = kv_proj.view(B, S, self.num_heads, self.qk_nope_head_dim + self.v_head_dim)
452
+ k_pass, v_states = torch.split(
453
+ kv_proj, [self.qk_nope_head_dim, self.v_head_dim], dim=-1
454
+ ) # [B,S,H,nope], [B,S,H,V]
455
+
456
+ # Rope on K side: keep a single-head rope stream like MLA, then expand
457
+ k_rot = k_rot.view(B, 1, S, self.qk_rope_head_dim) # [B, 1, S, rope_D]
458
+ k_rot = apply_rotary_pos_emb(k_rot, cos, sin) # [B, 1, S, rope_D]
459
+
460
+ # Concatenate K = [K_pass, K_rot(expanded)]
461
+ k_states = torch.cat(
462
+ (
463
+ k_pass.transpose(1, 2), # [B, H, S, nope_D]
464
+ k_rot.expand(B, self.num_heads, S, -1),
465
+ ), # [B, H, S, rope_D]
466
+ dim=-1,
467
+ ) # [B, H, S, D]
468
+ v_states = v_states.transpose(1, 2).contiguous() # [B, H, S, V]
469
+
470
+ # ----- Cache update/usage -----
471
+ if past_key_values is not None:
472
+ # Store compressed stream & rope stream (as in original MLA path)
473
+ # We cache `kv_compressed` under `keys` and `k_rot` under `values` in MlaLayer.
474
+ # Shapes must be [B, H, t, *] and [B, 1, t, rope_D].
475
+ kv_comp_cache = kv_compressed.view(B, 1, S, self.kv_lora_rank).expand(B, self.num_heads, S, -1)
476
+ k_rot_cache = k_rot # [B, 1, S, rope_D]
477
+ cached_kv, cached_pe = past_key_values.update(
478
+ kv_comp_cache, k_rot_cache, layer_idx=self.layer_idx, cache_kwargs={"cache_position": cache_position}
479
+ )
480
+ # Decode path makes use of cached projections; Prefill can use full K/V directly.
481
+
482
+ # ----- Two paths (prefill vs decode) -----
483
+ if attention_mask is not None:
484
+ # Prefill (full attention over local window): standard scaled dot-product with top-k pruning from indexer
485
+
486
+ # Build scores: [B, H, S, S_total]
487
+ # K layout already [B, H, T, D]
488
+ scores = (q_states.float() @ k_states.float().transpose(-1, -2)) * self.scaling # [B, H, S, T]
489
+
490
+ # Indexer top-k
491
+ if past_key_values is not None:
492
+ topk_idx = self.indexer(
493
+ hidden_states,
494
+ q_resid,
495
+ position_embeddings,
496
+ attention_mask,
497
+ past_key_values_index=past_key_values, # we reuse same Cache with IndexerLayer? (separate cache recommended)
498
+ cache_position=cache_position,
499
+ )
500
+ # Build mask to keep only top-k per (B,S,head?)
501
+ # Expect topk_idx shape to broadcast to [B, H, S, T]. We scatter along last dim.
502
+ keep_mask = torch.full_like(scores, float("-inf"))
503
+ # If topk_idx is [B,S,topk], expand for heads:
504
+ if topk_idx.dim() == 3:
505
+ topk_idx = topk_idx.unsqueeze(1).expand(B, self.num_heads, S, -1)
506
+ keep_mask.scatter_(-1, topk_idx, 0.0)
507
+ scores = scores + keep_mask
508
+
509
+ probs = nn.functional.softmax(scores, dim=-1, dtype=torch.float32).type_as(hidden_states) # [B, H, S, T]
510
+ attn_output = probs @ v_states # [B, H, S, V]
511
+
512
+ elif past_key_values is not None:
513
+ # Decode: use cached compressed KV & rope stream to recompose attention scores efficiently
514
+ # Compose q_pass and q_rot pieces as in MLA math, but via matmul
515
+ # 1) Rebuild "nope" term via kv_b weights (dequant on the fly)
516
+ wkv_b = self.kv_b_proj.weight.view(
517
+ self.num_heads, self.qk_nope_head_dim + self.v_head_dim, self.kv_lora_rank
518
+ )
519
+ w_k_nope = wkv_b[:, : self.qk_nope_head_dim, :] # [H, nope_D, kv_rank]
520
+ w_v = wkv_b[:, self.qk_nope_head_dim :, :] # [H, V, kv_rank]
521
+
522
+ # q_pass: [B,H,S,nope_D]; cached_kv: [B,H,T,kv_rank]
523
+ q_pass = q_states[..., : self.qk_nope_head_dim] # [B,H,S,nope_D]
524
+ kv_comp = past_key_values[self.layer_idx][0] # keys -> [B,H,T,kv_rank]
525
+ pe_full = past_key_values[self.layer_idx][1] # values -> [B,1,T,rope_D]
526
+ # Project q_pass with w_k_nope: [B,H,S,kv_rank]
527
+ qk_nope = torch.matmul(q_pass, w_k_nope.transpose(-1, -2)) # [B,H,S,kv_rank]
528
+ # Scores_nope = qk_nope @ kv_comp^T
529
+ scores_nope = torch.matmul(qk_nope.float(), kv_comp.float().transpose(-1, -2)) # [B,H,S,T]
530
+
531
+ # 2) Rope term: q_rot @ k_rot^T
532
+ q_rot_only = q_states[..., -self.qk_rope_head_dim :] # [B,H,S,rope_D]
533
+ k_rot_only = pe_full.expand(B, self.num_heads, -1, -1) # [B,H,T,rope_D]
534
+ scores_rot = torch.matmul(q_rot_only.float(), k_rot_only.float().transpose(-1, -2)) # [B,H,S,T]
535
+
536
+ scores = (scores_nope + scores_rot) * self.scaling
537
+
538
+ # Indexer top-k (decode)
539
+ topk_idx = self.indexer(
540
+ hidden_states,
541
+ q_resid,
542
+ position_embeddings,
543
+ attention_mask,
544
+ past_key_values_index=past_key_values,
545
+ cache_position=cache_position,
546
+ )
547
+ # For decode single-step S==1 typically; build a [B,H,1,T] mask
548
+ keep_mask = torch.full_like(scores, float("-inf"))
549
+ if topk_idx.dim() == 3:
550
+ topk_idx = topk_idx.unsqueeze(1).expand(B, self.num_heads, S, -1)
551
+ keep_mask.scatter_(-1, topk_idx, 0.0)
552
+ scores = scores + keep_mask
553
+
554
+ probs = nn.functional.softmax(scores, dim=-1, dtype=torch.float32).type_as(hidden_states) # [B,H,S,T]
555
+
556
+ # Rebuild V for decode fast-path: v = (kv_comp @ w_v^T)
557
+ # kv_comp: [B,H,T,kv_rank], w_v: [H, V, kv_rank]
558
+ v_from_comp = torch.matmul(kv_comp, w_v.transpose(-1, -2)) # [B,H,T,V]
559
+ attn_output = torch.matmul(probs, v_from_comp) # [B,H,S,V]
560
+
561
+ # Output projection
562
+ attn_output = attn_output.transpose(1, 2).reshape(B, S, -1).contiguous() # [B,S,H*V]
563
+ attn_output = self.o_proj(attn_output) # [B,S,hidden]
564
+ return attn_output, None
565
+
566
+
567
+
568
+ class DeepseekV32MLP(nn.Module):
569
+ def __init__(self, config, intermediate_size=None):
570
+ super().__init__()
571
+ self.config = config
572
+ self.hidden_size = config.hidden_size
573
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
574
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
575
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
576
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
577
+ self.act_fn = ACT2FN[config.hidden_act]
578
+
579
+ def forward(self, x):
580
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
581
+ return down_proj
582
+
583
+
584
+ class DeepseekV32TopkRouter(nn.Module):
585
+ def __init__(self, config: DeepseekV32Config):
586
+ super().__init__()
587
+ self.config = config
588
+ self.top_k = config.num_experts_per_tok
589
+ self.n_routed_experts = config.n_routed_experts
590
+ self.routed_scaling_factor = config.routed_scaling_factor
591
+ self.n_group = config.n_group
592
+ self.topk_group = config.topk_group
593
+ self.norm_topk_prob = config.norm_topk_prob
594
+
595
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, config.hidden_size)))
596
+ self.register_buffer("e_score_correction_bias", torch.zeros((self.n_routed_experts), dtype=torch.float32))
597
+
598
+ def forward(self, hidden_states):
599
+ hidden_states = hidden_states.view(-1, self.config.hidden_size)
600
+ router_logits = F.linear(hidden_states.type(torch.float32), self.weight.type(torch.float32))
601
+ return router_logits
602
+
603
+
604
+ class DeepseekV32MoE(nn.Module):
605
+ """
606
+ A mixed expert module containing shared experts.
607
+ """
608
+
609
+ def __init__(self, config):
610
+ super().__init__()
611
+ self.config = config
612
+ self.experts = nn.ModuleList(
613
+ [
614
+ DeepseekV32MLP(config, intermediate_size=config.moe_intermediate_size)
615
+ for _ in range(config.n_routed_experts)
616
+ ]
617
+ )
618
+ self.gate = DeepseekV32TopkRouter(config)
619
+ self.shared_experts = DeepseekV32MLP(
620
+ config=config, intermediate_size=config.moe_intermediate_size * config.n_shared_experts
621
+ )
622
+
623
+ def moe(self, hidden_states: torch.Tensor, topk_indices: torch.Tensor, topk_weights: torch.Tensor):
624
+ r"""
625
+ CALL FOR CONTRIBUTION! I don't have time to optimise this right now, but expert weights need to be fused
626
+ to not have to do a loop here (deepseek has 256 experts soooo yeah).
627
+ """
628
+ final_hidden_states = torch.zeros_like(hidden_states, dtype=topk_weights.dtype)
629
+ expert_mask = torch.nn.functional.one_hot(topk_indices, num_classes=len(self.experts))
630
+ expert_mask = expert_mask.permute(2, 0, 1)
631
+
632
+ for expert_idx in range(len(self.experts)):
633
+ expert = self.experts[expert_idx]
634
+ mask = expert_mask[expert_idx]
635
+ token_indices, weight_indices = torch.where(mask)
636
+
637
+ if token_indices.numel() > 0:
638
+ expert_weights = topk_weights[token_indices, weight_indices]
639
+ expert_input = hidden_states[token_indices]
640
+ expert_output = expert(expert_input)
641
+ weighted_output = expert_output * expert_weights.unsqueeze(-1)
642
+ final_hidden_states.index_add_(0, token_indices, weighted_output)
643
+
644
+ # in original deepseek, the output of the experts are gathered once we leave this module
645
+ # thus the moe module is itelsf an IsolatedParallel module
646
+ # and all expert are "local" meaning we shard but we don't gather
647
+ return final_hidden_states.type(hidden_states.dtype)
648
+
649
+ def forward(self, hidden_states):
650
+ residuals = hidden_states
651
+ orig_shape = hidden_states.shape
652
+ topk_indices, topk_weights = self.gate(hidden_states)
653
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
654
+ hidden_states = self.moe(hidden_states, topk_indices, topk_weights).view(*orig_shape)
655
+ hidden_states = hidden_states + self.shared_experts(residuals)
656
+ return hidden_states
657
+
658
+
659
+
660
+ class DeepseekV32DecoderLayer(GradientCheckpointingLayer):
661
+ def __init__(self, config: DeepseekV32Config, layer_idx: int):
662
+ super().__init__()
663
+ self.hidden_size = config.hidden_size
664
+ self.self_attn = DeepseekV32Attention(config, layer_idx)
665
+
666
+ if config.mlp_layer_types[layer_idx] == "sparse":
667
+ self.mlp = DeepseekV32MoE(config)
668
+ else:
669
+ self.mlp = DeepseekV32MLP(config)
670
+
671
+ self.input_layernorm = DeepseekV32RMSNorm(config.hidden_size, config.rms_norm_eps)
672
+ self.post_attention_layernorm = DeepseekV32RMSNorm(config.hidden_size, config.rms_norm_eps)
673
+
674
+ def forward(
675
+ self,
676
+ hidden_states: torch.Tensor,
677
+ attention_mask: torch.Tensor | None = None,
678
+ position_ids: torch.LongTensor | None = None,
679
+ past_key_values: Cache | None = None,
680
+ use_cache: bool | None = False,
681
+ cache_position: torch.LongTensor | None = None,
682
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
683
+ **kwargs: Unpack[TransformersKwargs],
684
+ ) -> torch.Tensor:
685
+ residual = hidden_states
686
+ hidden_states = self.input_layernorm(hidden_states)
687
+ # Self Attention
688
+ hidden_states, _ = self.self_attn(
689
+ hidden_states=hidden_states,
690
+ attention_mask=attention_mask,
691
+ position_ids=position_ids,
692
+ past_key_values=past_key_values,
693
+ use_cache=use_cache,
694
+ cache_position=cache_position,
695
+ position_embeddings=position_embeddings,
696
+ **kwargs,
697
+ )
698
+ hidden_states = residual + hidden_states
699
+
700
+ # Fully Connected
701
+ residual = hidden_states
702
+ hidden_states = self.post_attention_layernorm(hidden_states)
703
+ hidden_states = self.mlp(hidden_states)
704
+ hidden_states = residual + hidden_states
705
+ return hidden_states
706
+
707
+
708
+ class DeepseekV32PreTrainedModel(PreTrainedModel):
709
+ config: DeepseekV32Config
710
+ base_model_prefix = "model"
711
+ supports_gradient_checkpointing = True
712
+ _no_split_modules = ["DeepseekV32DecoderLayer"]
713
+ _skip_keys_device_placement = ["past_key_values"]
714
+ _supports_flash_attn = True
715
+ _supports_sdpa = True
716
+ _supports_flex_attn = True
717
+ _can_compile_fullgraph = False
718
+ _supports_attention_backend = True
719
+ _can_record_outputs = {
720
+ "hidden_states":DeepseekV32DecoderLayer,
721
+ "attentions": DeepseekV32Attention,
722
+ }
723
+
724
+ @torch.no_grad()
725
+ def _init_weights(self, module):
726
+ super()._init_weights(module)
727
+ if isinstance(module, DeepseekV32TopkRouter):
728
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
729
+
730
+
731
+ class DeepseekV32RotaryEmbedding(nn.Module):
732
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
733
+
734
+ def __init__(self, config: DeepseekV32Config, device=None):
735
+ super().__init__()
736
+ self.max_seq_len_cached = config.max_position_embeddings
737
+ self.original_max_seq_len = config.max_position_embeddings
738
+
739
+ self.config = config
740
+
741
+ self.rope_type = self.config.rope_scaling["rope_type"]
742
+ rope_init_fn: Callable = self.compute_default_rope_parameters
743
+ if self.rope_type != "default":
744
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
745
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
746
+
747
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
748
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
749
+
750
+ @staticmethod
751
+ def compute_default_rope_parameters(
752
+ config: DeepseekV32Config | None = None,
753
+ device: Optional["torch.device"] = None,
754
+ seq_len: int | None = None,
755
+ ) -> tuple["torch.Tensor", float]:
756
+ """
757
+ Computes the inverse frequencies according to the original RoPE implementation
758
+ Args:
759
+ config ([`~transformers.PreTrainedConfig`]):
760
+ The model configuration.
761
+ device (`torch.device`):
762
+ The device to use for initialization of the inverse frequencies.
763
+ seq_len (`int`, *optional*):
764
+ The current sequence length. Unused for this type of RoPE.
765
+ Returns:
766
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
767
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
768
+ """
769
+ base = config.rope_theta
770
+ partial_rotary_factor = config.rope_scaling.get("partial_rotary_factor", 1.0)
771
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
772
+ dim = int(head_dim * partial_rotary_factor)
773
+
774
+ attention_factor = 1.0 # Unused in this type of RoPE
775
+
776
+ # Compute the inverse frequencies
777
+ inv_freq = 1.0 / (
778
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
779
+ )
780
+ return inv_freq, attention_factor
781
+
782
+ @torch.no_grad()
783
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
784
+ def forward(self, x, position_ids):
785
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
786
+ position_ids_expanded = position_ids[:, None, :].float()
787
+
788
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
789
+ with torch.autocast(device_type=device_type, enabled=False): # Force float32
790
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
791
+ emb = torch.cat((freqs, freqs), dim=-1)
792
+ cos = emb.cos() * self.attention_scaling
793
+ sin = emb.sin() * self.attention_scaling
794
+
795
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
796
+
797
+
798
+ class DeepseekV32Model(DeepseekV32PreTrainedModel):
799
+ _keys_to_ignore_on_load_unexpected = [r"model\.layers\.78.*"]
800
+
801
+ def __init__(self, config: DeepseekV32Config):
802
+ super().__init__(config)
803
+ self.padding_idx = config.pad_token_id
804
+ self.vocab_size = config.vocab_size
805
+
806
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
807
+ self.layers = nn.ModuleList(
808
+ [DeepseekV32DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
809
+ )
810
+ self.norm = DeepseekV32RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
811
+ self.rotary_emb = DeepseekV32RotaryEmbedding(config=config)
812
+ self.gradient_checkpointing = False
813
+
814
+ # Initialize weights and apply final processing
815
+ self.post_init()
816
+
817
+ @check_model_inputs
818
+ def forward(
819
+ self,
820
+ input_ids: torch.LongTensor | None = None,
821
+ attention_mask: torch.Tensor | None = None,
822
+ position_ids: torch.LongTensor | None = None,
823
+ past_key_values: Cache | None = None,
824
+ inputs_embeds: torch.FloatTensor | None = None,
825
+ cache_position: torch.LongTensor | None = None,
826
+ use_cache: bool | None = None,
827
+ **kwargs: Unpack[TransformersKwargs],
828
+ ) -> BaseModelOutputWithPast:
829
+ if (input_ids is None) ^ (inputs_embeds is not None):
830
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
831
+
832
+ if inputs_embeds is None:
833
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
834
+
835
+ if use_cache and past_key_values is None:
836
+ past_key_values = DynamicCache(config=self.config)
837
+
838
+ if cache_position is None:
839
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
840
+ cache_position: torch.Tensor = (
841
+ torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
842
+ )
843
+
844
+ if position_ids is None:
845
+ position_ids = cache_position.unsqueeze(0)
846
+
847
+ causal_mask = create_causal_mask(
848
+ config=self.config,
849
+ input_embeds=inputs_embeds,
850
+ attention_mask=attention_mask,
851
+ cache_position=cache_position,
852
+ past_key_values=past_key_values,
853
+ position_ids=position_ids,
854
+ )
855
+
856
+ hidden_states = inputs_embeds
857
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
858
+
859
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
860
+ hidden_states = decoder_layer(
861
+ hidden_states,
862
+ attention_mask=causal_mask,
863
+ position_embeddings=position_embeddings,
864
+ position_ids=position_ids,
865
+ past_key_values=past_key_values,
866
+ use_cache=use_cache,
867
+ cache_position=cache_position,
868
+ **kwargs,
869
+ )
870
+
871
+ hidden_states = self.norm(hidden_states)
872
+ return BaseModelOutputWithPast(
873
+ last_hidden_state=hidden_states,
874
+ past_key_values=past_key_values,
875
+ )
876
+
877
+
878
+ class DeepseekV32ForCausalLM(DeepseekV32PreTrainedModel, GenerationMixin):
879
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
880
+ _tp_plan = {"lm_head": "colwise_gather_output"}
881
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
882
+
883
+ def __init__(self, config):
884
+ super().__init__(config)
885
+ self.model = DeepseekV32Model(config)
886
+ self.vocab_size = config.vocab_size
887
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
888
+
889
+ # Initialize weights and apply final processing
890
+ self.post_init()
891
+
892
+ @can_return_tuple
893
+ def forward(
894
+ self,
895
+ input_ids: torch.LongTensor | None = None,
896
+ attention_mask: torch.Tensor | None = None,
897
+ position_ids: torch.LongTensor | None = None,
898
+ past_key_values: Cache | None = None,
899
+ inputs_embeds: torch.FloatTensor | None = None,
900
+ labels: torch.LongTensor | None = None,
901
+ use_cache: bool | None = None,
902
+ cache_position: torch.LongTensor | None = None,
903
+ logits_to_keep: int | torch.Tensor = 0,
904
+ **kwargs: Unpack[TransformersKwargs],
905
+ ) -> CausalLMOutputWithPast:
906
+ outputs: BaseModelOutputWithPast = self.model(
907
+ input_ids=input_ids,
908
+ attention_mask=attention_mask,
909
+ position_ids=position_ids,
910
+ past_key_values=past_key_values,
911
+ inputs_embeds=inputs_embeds,
912
+ use_cache=use_cache,
913
+ cache_position=cache_position,
914
+ **kwargs,
915
+ )
916
+
917
+ hidden_states = outputs.last_hidden_state
918
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
919
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
920
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
921
+
922
+ loss = None
923
+ if labels is not None:
924
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
925
+
926
+ return CausalLMOutputWithPast(
927
+ loss=loss,
928
+ logits=logits,
929
+ past_key_values=outputs.past_key_values,
930
+ hidden_states=outputs.hidden_states,
931
+ attentions=outputs.attentions,
932
+ )
933
+
934
+
935
+ __all__ = ["DeepseekV32PreTrainedModel", "DeepseekV32Model", "DeepseekV32ForCausalLM"]
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_eos_token": false,
4
+ "bos_token": {
5
+ "__type": "AddedToken",
6
+ "content": "<|begin▁of▁sentence|>",
7
+ "lstrip": false,
8
+ "normalized": true,
9
+ "rstrip": false,
10
+ "single_word": false
11
+ },
12
+ "clean_up_tokenization_spaces": false,
13
+ "eos_token": {
14
+ "__type": "AddedToken",
15
+ "content": "<|end▁of▁sentence|>",
16
+ "lstrip": false,
17
+ "normalized": true,
18
+ "rstrip": false,
19
+ "single_word": false
20
+ },
21
+ "legacy": true,
22
+ "model_max_length": 131072,
23
+ "pad_token": {
24
+ "__type": "AddedToken",
25
+ "content": "<|end▁of▁sentence|>",
26
+ "lstrip": false,
27
+ "normalized": true,
28
+ "rstrip": false,
29
+ "single_word": false
30
+ },
31
+ "sp_model_kwargs": {},
32
+ "unk_token": null,
33
+ "tokenizer_class": "LlamaTokenizerFast"
34
+ }