poe_api_process 0.4.6

Poe API for rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
use crate::types::{
    ChatMessage, ChatRequest, ChatTool, ChatToolCall, ChatToolResult, FunctionCall,
};
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};

// 全局工具調用 ID 計數器,確保每個工具調用都有唯一的 ID
static GLOBAL_CALL_ID: AtomicU64 = AtomicU64::new(1);

// 生成下一個唯一的工具調用 ID
fn get_next_call_id() -> u64 {
    GLOBAL_CALL_ID.fetch_add(1, Ordering::SeqCst)
}

#[cfg(feature = "trace")]
fn safe_string_truncate(s: &str, max_bytes: usize) -> &str {
    if s.len() <= max_bytes {
        return s;
    }

    // 從 max_bytes 位置向前查找,直到找到有效的字符邊界
    let mut end = max_bytes;
    while end > 0 && !s.is_char_boundary(end) {
        end -= 1;
    }

    &s[..end]
}

// XML 工具格式相關結構
#[derive(Debug, Clone)]
pub struct XmlTool {
    pub name: String,
    pub description: Option<String>,
    pub parameters: Vec<XmlParameter>,
}

#[derive(Debug, Clone)]
pub struct XmlParameter {
    pub name: String,
    pub param_type: String,
    pub description: Option<String>,
    pub required: bool,
    pub enum_values: Option<Vec<String>>,
}

// XML 工具轉換 trait
pub trait ToXml {
    fn to_xml(&self) -> String;
}

impl ToXml for ChatTool {
    fn to_xml(&self) -> String {
        let mut xml = String::new();
        xml.push_str(&format!("<{}>", self.function.name));

        if let Some(ref description) = self.function.description {
            xml.push_str(&format!(
                "\n<description>{}</description>",
                escape_xml(description)
            ));
        }

        if let Some(ref parameters) = self.function.parameters {
            xml.push_str("\n<parameters>");

            if let Some(properties) = parameters.properties.as_object() {
                for (param_name, param_value) in properties {
                    xml.push_str(&format!(
                        "\n<{}_name>{}</{}_name>",
                        param_name, param_name, param_name
                    ));

                    if let Some(param_type) = param_value.get("type").and_then(|v| v.as_str()) {
                        xml.push_str(&format!(
                            "\n<{}_type>{}</{}_type>",
                            param_name, param_type, param_name
                        ));
                    }

                    if let Some(param_desc) =
                        param_value.get("description").and_then(|v| v.as_str())
                    {
                        xml.push_str(&format!(
                            "\n<{}_description>{}</{}_description>",
                            param_name,
                            escape_xml(param_desc),
                            param_name
                        ));
                    }

                    let is_required = parameters.required.contains(param_name);
                    xml.push_str(&format!(
                        "\n<{}_required>{}</{}_required>",
                        param_name, is_required, param_name
                    ));

                    if let Some(enum_values) = param_value.get("enum").and_then(|v| v.as_array()) {
                        xml.push_str(&format!("\n<{}_enum>", param_name));
                        for enum_val in enum_values {
                            if let Some(val_str) = enum_val.as_str() {
                                xml.push_str(&format!(
                                    "\n<option>{}</option>",
                                    escape_xml(val_str)
                                ));
                            }
                        }
                        xml.push_str(&format!("\n</{}_enum>", param_name));
                    }
                }
            }

            xml.push_str("\n</parameters>");
        }

        xml.push_str(&format!("\n</{}>", self.function.name));
        xml
    }
}

impl ToXml for Vec<ChatTool> {
    fn to_xml(&self) -> String {
        if self.is_empty() {
            return String::new();
        }

        let mut xml = String::from("\n\n<tools>");
        for tool in self {
            xml.push('\n');
            xml.push_str(&tool.to_xml());
        }
        xml.push_str("\n</tools>");
        xml
    }
}

impl ToXml for ChatToolResult {
    fn to_xml(&self) -> String {
        let mut xml = String::new();
        xml.push_str(&format!(
            "  <result tool_call_id=\"{}\">",
            escape_xml(&self.tool_call_id)
        ));

        // 檢查內容是否為錯誤格式
        if self.content.trim().starts_with("ERROR:") || self.content.trim().starts_with("Error:") {
            xml.push_str("\n    <error>");
            xml.push_str(&escape_xml(&self.content));
            xml.push_str("</error>");
        } else {
            xml.push_str("\n    <output>");
            xml.push_str(&escape_xml(&self.content));
            xml.push_str("</output>");
        }

        xml.push_str("\n  </result>");
        xml
    }
}

impl ToXml for Vec<ChatToolResult> {
    fn to_xml(&self) -> String {
        if self.is_empty() {
            return String::new();
        }

        let mut xml = String::from("\n\n<tool_results>");
        for result in self {
            xml.push('\n');
            xml.push_str(&result.to_xml());
        }
        xml.push_str("\n</tool_results>");
        xml
    }
}

// XML 轉義函數
fn escape_xml(text: &str) -> String {
    text.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&apos;")
}

// 為 ChatMessage 添加 XML 工具附加功能(僅內部使用)
impl ChatMessage {
    /// 將 XML 格式的工具定義附加到消息內容末尾(內部使用)
    pub(crate) fn append_xml_tools(&mut self, tools: &[ChatTool]) {
        if !tools.is_empty() {
            let tools_vec = tools.to_vec();
            let xml_tools = tools_vec.to_xml();
            self.content.push_str(&xml_tools);
        }
    }

    /// 將 XML 格式的工具結果附加到消息內容末尾(內部使用)
    pub(crate) fn append_xml_tool_results(&mut self, tool_results: &[ChatToolResult]) {
        if !tool_results.is_empty() {
            let results_vec = tool_results.to_vec();
            let xml_results = results_vec.to_xml();
            self.content.push_str(&xml_results);
        }
    }
}

// 為 ChatRequest 添加 XML 工具處理功能(僅內部使用)
impl ChatRequest {
    /// 將工具轉換為 XML 格式並附加到最後一條用戶消息中(內部使用)
    pub(crate) fn append_tools_as_xml(&mut self) {
        if let Some(ref tools) = self.tools {
            if !tools.is_empty() {
                // 找到最後一條用戶消息
                for message in self.query.iter_mut().rev() {
                    if message.role == "user" {
                        // 添加完整的工具使用提示詞
                        let tool_usage_prompt = r#"

You are a powerful AI assistant. Your core mission is to accurately and efficiently answer user questions and execute tasks.

To achieve this, you have been given a set of tools. When you determine that using a tool can fetch real-time information, perform a specific action, or provide a more precise answer than your built-in knowledge allows, you MUST proactively use these tools. Do not rely solely on your training data.

Tool Calling Rules:

1.  Be Proactive: Actively look for opportunities to use your tools. If you think a tool might help the user, use it.

2.  Strict Formatting: All tool calls must strictly adhere to the following XML format. This is not a suggestion; it is a mandatory requirement.

XML Calling Format Example:

When you need to call a tool, your response MUST ONLY contain XML blocks with the following structure.

<tool_call>

  <invoke name="tool_name">

    <parameter name="parameter_1_name">value_for_parameter_1</parameter>

    <parameter name="parameter_2_name">value_for_parameter_2</parameter>

    <!-- Add more parameters as needed -->

  </invoke>

</tool_call>

<!-- If you need to call multiple tools at once, you can place multiple <tool_call> blocks sequentially like this -->

<tool_call>

  <invoke name="another_tool_name">

    <parameter name="parameter_A">value_A</parameter>

  </invoke>

</tool_call>

Explanation:

- <tool_call>: The outermost wrapper for each individual tool call.

- <invoke name="...">: The name attribute must be the exact name of the tool you are calling.

- <parameter name="...">: The name attribute is the name of the parameter the tool requires, and the content between the tags is its value. All parameter values must be properly XML-escaped (e.g., & must be written as &amp;).

Now, begin your work based on the user's next prompt. Remember, you are a problem-solver, and your tools are your most powerful weapons.
"#;
                        message.content.push_str(tool_usage_prompt);
                        message.append_xml_tools(tools);
                        break;
                    }
                }
            }
        }
    }

    /// 將工具結果以 XML 格式附加到最後一條用戶消息中(內部使用)
    pub(crate) fn append_tool_results_as_xml(&mut self) {
        if let Some(ref tool_results) = self.tool_results {
            if !tool_results.is_empty() {
                // 找到最後一條用戶消息
                for message in self.query.iter_mut().rev() {
                    if message.role == "user" {
                        // 添加工具結果分析提示詞
                        let tool_results_prompt = r#"

You have previously requested one or more tool calls. The results are now available. Your new task is to analyze these results and formulate a final, comprehensive answer for the user in natural language.

The tool results are provided to you in the following XML format:

**Your Instructions:**

1.  **Analyze the Results**: Carefully examine the content within the `<output>` or `<error>` tags for each result.
2.  **Synthesize, Don't Recite**: Do not just repeat the raw tool output (like raw JSON). You **must interpret** the data, synthesize information if there are multiple results, and present it to the user in a clear, conversational, and helpful way.
3.  **Formulate the Final Answer**: Your response should be the complete and final answer to the user's original query. Do not output any more `<tool_call>` blocks unless the results explicitly indicate a necessary follow-up action.
4.  **Handle Errors Gracefully**: If a tool returned an error, politely inform the user that you were unable to retrieve that specific piece of information and, if appropriate, briefly explain the issue (e.g., "I couldn't find information for that city.").
"#;
                        message.content.push_str(tool_results_prompt);
                        message.append_xml_tool_results(tool_results);
                        break;
                    }
                }
            }
        }
    }
}

// XML 工具調用解析功能
pub struct XmlToolCallParser;

impl XmlToolCallParser {
    /// 從文本中解析 XML 工具調用
    pub fn parse_xml_tool_calls(text: &str) -> Vec<ChatToolCall> {
        let mut tool_calls = Vec::new();

        #[cfg(feature = "trace")]
        {
            use tracing::debug;
            debug!("開始解析 XML 工具調用,文本長度: {}", text.len());
            debug!("文本內容預覽: {}", text);
        }

        // 首先查找 <tool_call> 包裝的工具調用
        let mut current_pos = 0;
        while let Some(call_start) = text[current_pos..].find("<tool_call>") {
            let actual_start = current_pos + call_start;

            if let Some(call_end) = text[actual_start..].find("</tool_call>") {
                let actual_end = actual_start + call_end + "</tool_call>".len();
                let call_content = &text[actual_start..actual_end];

                let current_call_id = get_next_call_id();
                #[cfg(feature = "trace")]
                {
                    use tracing::debug;
                    debug!(
                        "找到完整的工具調用 #{}, 開始位置: {}, 結束位置: {}",
                        current_call_id, actual_start, actual_end
                    );
                    debug!("工具調用內容: {}", call_content);
                }

                if let Some(tool_call) = Self::parse_single_tool_call(call_content, current_call_id)
                {
                    #[cfg(feature = "trace")]
                    {
                        use tracing::debug;
                        debug!(
                            "成功解析工具調用 #{}: {}",
                            current_call_id, tool_call.function.name
                        );
                    }
                    tool_calls.push(tool_call);
                } else {
                    #[cfg(feature = "trace")]
                    {
                        use tracing::debug;
                        debug!("無法解析工具調用 #{}", current_call_id);
                    }
                }

                current_pos = actual_end;
            } else {
                #[cfg(feature = "trace")]
                {
                    use tracing::debug;
                    debug!("找到 <tool_call> 但沒有找到對應的 </tool_call>,停止解析");
                }
                break;
            }
        }

        // 如果沒有找到 <tool_call> 包裝的調用,直接查找 <invoke> 標籤
        if tool_calls.is_empty() {
            current_pos = 0;
            while let Some(invoke_start) = text[current_pos..].find("<invoke") {
                let actual_start = current_pos + invoke_start;

                if let Some(invoke_end) = text[actual_start..].find("</invoke>") {
                    let actual_end = actual_start + invoke_end + "</invoke>".len();
                    let invoke_content = &text[actual_start..actual_end];

                    let current_call_id = get_next_call_id();
                    #[cfg(feature = "trace")]
                    {
                        use tracing::debug;
                        debug!(
                            "找到直接的 invoke 調用 #{}, 開始位置: {}, 結束位置: {}",
                            current_call_id, actual_start, actual_end
                        );
                        debug!("invoke 調用內容: {}", invoke_content);
                    }

                    if let Some(tool_call) =
                        Self::parse_single_tool_call(invoke_content, current_call_id)
                    {
                        #[cfg(feature = "trace")]
                        {
                            use tracing::debug;
                            debug!(
                                "成功解析直接 invoke 調用 #{}: {}",
                                current_call_id, tool_call.function.name
                            );
                        }
                        tool_calls.push(tool_call);
                    } else {
                        #[cfg(feature = "trace")]
                        {
                            use tracing::debug;
                            debug!("無法解析直接 invoke 調用 #{}", current_call_id);
                        }
                    }

                    current_pos = actual_end;
                } else {
                    #[cfg(feature = "trace")]
                    {
                        use tracing::debug;
                        debug!("找到 <invoke 但沒有找到對應的 </invoke>,停止解析");
                    }
                    break;
                }
            }
        }

        #[cfg(feature = "trace")]
        {
            use tracing::debug;
            debug!(
                "XML 工具調用解析完成,共找到 {} 個工具調用",
                tool_calls.len()
            );
        }

        tool_calls
    }

    /// 基於提供的工具定義從文本中解析 XML 工具調用
    pub fn parse_xml_tool_calls_with_tools(text: &str, tools: &[ChatTool]) -> Vec<ChatToolCall> {
        let mut tool_calls = Vec::new();

        // 首先嘗試標準格式
        tool_calls.extend(Self::parse_xml_tool_calls(text));

        // 如果沒有找到標準格式的工具調用,嘗試基於工具定義的解析
        if tool_calls.is_empty() {
            tool_calls.extend(Self::parse_tool_specific_xml_format(text, tools));
        } else {
            // 如果已經找到了標準格式的工具調用,但還想嘗試工具特定格式
            let additional_calls = Self::parse_tool_specific_xml_format(text, tools);

            // 只添加那些在標準格式中沒有找到的工具調用
            for additional_call in additional_calls {
                let already_exists = tool_calls.iter().any(|existing| {
                    existing.function.name == additional_call.function.name
                        && existing.function.arguments == additional_call.function.arguments
                });

                if !already_exists {
                    tool_calls.push(additional_call);
                }
            }
        }

        tool_calls
    }

    /// 解析單個工具調用
    fn parse_single_tool_call(xml_content: &str, call_id: u64) -> Option<ChatToolCall> {
        #[cfg(feature = "trace")]
        {
            use tracing::debug;
            debug!("嘗試解析單個工具調用,內容長度: {}", xml_content.len());
            debug!("XML 內容預覽: {}", safe_string_truncate(xml_content, 200));
        }

        // 首先嘗試解析 <invoke name="tool_name"> 格式
        if let Some(function_name) = Self::extract_invoke_name(xml_content) {
            let arguments = Self::extract_parameters_as_json(xml_content);

            #[cfg(feature = "trace")]
            {
                use tracing::debug;
                debug!(
                    "成功解析 invoke 格式,工具名: {}, 參數: {}",
                    function_name, arguments
                );
            }

            return Some(ChatToolCall {
                id: format!("call_{}", call_id),
                r#type: "function".to_string(),
                function: FunctionCall {
                    name: function_name,
                    arguments,
                },
            });
        }

        // 如果沒有找到 invoke 標籤,嘗試舊格式
        if let Some(function_name) = Self::extract_xml_value(xml_content, "name") {
            let arguments = Self::extract_xml_value(xml_content, "arguments")
                .unwrap_or_else(|| Self::extract_parameters_as_json(xml_content));

            #[cfg(feature = "trace")]
            {
                use tracing::debug;
                debug!(
                    "成功解析舊格式,工具名: {}, 參數: {}",
                    function_name, arguments
                );
            }

            return Some(ChatToolCall {
                id: format!("call_{}", call_id),
                r#type: "function".to_string(),
                function: FunctionCall {
                    name: function_name,
                    arguments,
                },
            });
        }

        // 嘗試直接工具名稱標籤格式
        if let Some((function_name, tool_content)) =
            Self::extract_direct_tool_name_and_content(xml_content)
        {
            let arguments = Self::extract_parameters_as_json(&tool_content);

            #[cfg(feature = "trace")]
            {
                use tracing::debug;
                debug!(
                    "成功解析直接工具名稱格式,工具名: {}, 參數: {}",
                    function_name, arguments
                );
            }

            return Some(ChatToolCall {
                id: format!("call_{}", call_id),
                r#type: "function".to_string(),
                function: FunctionCall {
                    name: function_name,
                    arguments,
                },
            });
        }

        #[cfg(feature = "trace")]
        {
            use tracing::debug;
            debug!("無法解析工具調用,未找到有效的工具名稱");
        }

        None
    }

    /// 提取直接工具名稱標籤格式並返回工具名稱和內容
    fn extract_direct_tool_name_and_content(xml_content: &str) -> Option<(String, String)> {
        // 跳過 <tool_call> 標籤,查找內部的工具標籤
        let start_marker = "<tool_call>";
        let end_marker = "</tool_call>";

        if let Some(start_pos) = xml_content.find(start_marker) {
            let content_start = start_pos + start_marker.len();
            if let Some(end_pos) = xml_content.find(end_marker) {
                let inner_content = &xml_content[content_start..end_pos];

                // 查找第一個非空白字符後的 < 標籤
                let trimmed = inner_content.trim();
                if trimmed.starts_with('<') {
                    // 找到第一個 >
                    if let Some(tag_end) = trimmed.find('>') {
                        let tag_content = &trimmed[1..tag_end];

                        // 排除特殊標籤
                        if !tag_content.starts_with('/')
                            && !tag_content.starts_with('!')
                            && !tag_content.contains("invoke")
                            && !tag_content.contains("parameter")
                            && !tag_content.contains(' ')
                        {
                            // 找到對應的結束標籤
                            let end_tag = format!("</{}>", tag_content);
                            if let Some(tool_end_pos) = trimmed.find(&end_tag) {
                                let tool_content = &trimmed[tag_end + 1..tool_end_pos];

                                #[cfg(feature = "trace")]
                                {
                                    use tracing::debug;
                                    debug!("提取到直接工具名稱: {}", tag_content);
                                    debug!("工具內容: {}", tool_content);
                                }

                                return Some((tag_content.to_string(), tool_content.to_string()));
                            }
                        }
                    }
                }
            }
        }

        None
    }

    /// 基於提供的工具定義解析 XML 格式
    fn parse_tool_specific_xml_format(text: &str, tools: &[ChatTool]) -> Vec<ChatToolCall> {
        let mut tool_calls = Vec::new();

        for tool in tools {
            // 解析該工具的所有調用實例
            let mut current_pos = 0;
            while let Some(tool_call) = Self::parse_tool_tag_from_position(
                text,
                &tool.function.name,
                get_next_call_id(),
                current_pos,
            ) {
                tool_calls.push(tool_call);

                // 更新搜索位置,避免重複解析同一個工具調用
                if let Some(start_tag_pos) =
                    text[current_pos..].find(&format!("<{}>", tool.function.name))
                {
                    current_pos += start_tag_pos + format!("<{}>", tool.function.name).len();
                } else {
                    break;
                }
            }
        }

        tool_calls
    }

    /// 從指定位置開始解析特定工具標籤
    fn parse_tool_tag_from_position(
        text: &str,
        tool_name: &str,
        call_id: u64,
        start_from: usize,
    ) -> Option<ChatToolCall> {
        let start_tag = format!("<{}>", tool_name);
        let end_tag = format!("</{}>", tool_name);

        if let Some(start_pos) = text[start_from..].find(&start_tag) {
            let actual_start = start_from + start_pos;
            let content_start = actual_start + start_tag.len();
            if let Some(end_pos) = text[content_start..].find(&end_tag) {
                let tool_content = &text[content_start..content_start + end_pos];
                let arguments = Self::extract_parameters_as_json(tool_content);

                return Some(ChatToolCall {
                    id: format!("call_{}", call_id),
                    r#type: "function".to_string(),
                    function: FunctionCall {
                        name: tool_name.to_string(),
                        arguments,
                    },
                });
            }
        }

        None
    }

    /// 從 XML 中提取指定標籤的值
    fn extract_xml_value(xml: &str, tag: &str) -> Option<String> {
        let start_tag = format!("<{}>", tag);
        let end_tag = format!("</{}>", tag);

        if let Some(start) = xml.find(&start_tag) {
            let content_start = start + start_tag.len();
            if let Some(end) = xml[content_start..].find(&end_tag) {
                return Some(xml[content_start..content_start + end].trim().to_string());
            }
        }
        None
    }

    /// 從 <invoke name="tool_name"> 格式中提取工具名稱
    fn extract_invoke_name(xml: &str) -> Option<String> {
        // 查找 <invoke name="..."> 模式
        if let Some(invoke_start) = xml.find("<invoke") {
            let invoke_content = &xml[invoke_start..];
            if let Some(name_start) = invoke_content.find("name=\"") {
                let name_content_start = invoke_start + name_start + 6; // 6 = len("name=\"")
                if let Some(name_end) = xml[name_content_start..].find('"') {
                    return Some(
                        xml[name_content_start..name_content_start + name_end].to_string(),
                    );
                }
            }
        }
        None
    }

    /// 將 XML 參數轉換為 JSON 格式
    fn extract_parameters_as_json(xml_content: &str) -> String {
        let mut params = HashMap::new();

        // 首先嘗試解析 <parameter name="key">value</parameter> 格式
        let mut current_pos = 0;
        while let Some(param_start) = xml_content[current_pos..].find("<parameter") {
            let actual_start = current_pos + param_start;

            // 提取參數名
            if let Some(name_start) = xml_content[actual_start..].find("name=\"") {
                let name_content_start = actual_start + name_start + 6; // 6 = len("name=\"")
                if let Some(name_end) = xml_content[name_content_start..].find('"') {
                    let param_name =
                        xml_content[name_content_start..name_content_start + name_end].to_string();

                    // 找到參數值
                    if let Some(value_start) =
                        xml_content[name_content_start + name_end..].find('>')
                    {
                        let value_content_start = name_content_start + name_end + value_start + 1;
                        if let Some(value_end) =
                            xml_content[value_content_start..].find("</parameter>")
                        {
                            let param_value = xml_content
                                [value_content_start..value_content_start + value_end]
                                .trim();
                            if !param_value.is_empty() {
                                // 解碼 XML 實體
                                let decoded_value = Self::decode_xml_entities(param_value);
                                params.insert(param_name, decoded_value);
                            }
                        }
                    }
                }
            }

            current_pos = actual_start + 1;
        }

        // 如果沒有找到 parameter 標籤,嘗試解析所有標籤作為參數
        if params.is_empty() {
            let mut current_pos = 0;
            while current_pos < xml_content.len() {
                if let Some(tag_start) = xml_content[current_pos..].find('<') {
                    let actual_start = current_pos + tag_start;

                    // 跳過結束標籤、註釋和特殊標籤
                    if xml_content[actual_start..].starts_with("</")
                        || xml_content[actual_start..].starts_with("<!--")
                        || xml_content[actual_start..].starts_with("<invoke")
                        || xml_content[actual_start..].starts_with("<parameter")
                        || xml_content[actual_start..].starts_with("<tool_call")
                    {
                        current_pos = actual_start + 1;
                        continue;
                    }

                    // 找到標籤結束
                    if let Some(tag_end) = xml_content[actual_start + 1..].find('>') {
                        let tag_name = &xml_content[actual_start + 1..actual_start + 1 + tag_end];

                        // 跳過自閉合標籤和包含屬性的標籤
                        if tag_name.contains(' ') || tag_name.ends_with('/') {
                            current_pos = actual_start + 1 + tag_end + 1;
                            continue;
                        }

                        let content_start = actual_start + 1 + tag_end + 1;

                        // 找到對應的結束標籤
                        let end_tag = format!("</{}>", tag_name);
                        if let Some(end_pos) = xml_content[content_start..].find(&end_tag) {
                            let value = xml_content[content_start..content_start + end_pos].trim();
                            if !value.is_empty() {
                                let decoded_value = Self::decode_xml_entities(value);
                                params.insert(tag_name.to_string(), decoded_value);
                            }
                            current_pos = content_start + end_pos + end_tag.len();
                        } else {
                            current_pos = actual_start + 1;
                        }
                    } else {
                        current_pos = actual_start + 1;
                    }
                } else {
                    break;
                }
            }
        }

        // 轉換為 JSON
        if params.is_empty() {
            "{}".to_string()
        } else {
            serde_json::to_string(&params).unwrap_or_else(|_| "{}".to_string())
        }
    }

    /// 解碼 XML 實體
    fn decode_xml_entities(text: &str) -> String {
        text.replace("&lt;", "<")
            .replace("&gt;", ">")
            .replace("&amp;", "&")
            .replace("&quot;", "\"")
            .replace("&apos;", "'")
    }
}

// 為 ChatMessage 添加 XML 工具調用檢測功能
impl ChatMessage {
    /// 檢測消息中是否包含 XML 工具調用(通用格式)
    pub fn contains_xml_tool_calls(&self) -> bool {
        // 檢測標準的 <tool_call> 格式 - 必須有完整的開始和結束標籤
        if self.content.contains("<tool_call>") && self.content.contains("</tool_call>") {
            return true;
        }

        // 檢測 <invoke> 格式 - 必須有完整的開始和結束標籤
        if self.content.contains("<invoke") && self.content.contains("</invoke>") {
            return true;
        }

        false
    }

    /// 基於提供的工具定義檢測是否包含 XML 工具調用
    pub fn contains_xml_tool_calls_with_tools(&self, tools: &[ChatTool]) -> bool {
        // 首先檢查通用格式
        if self.contains_xml_tool_calls() {
            return true;
        }

        // 檢查特定工具的標籤
        for tool in tools {
            let tool_tag = format!("<{}>", tool.function.name);
            if self.content.contains(&tool_tag) {
                return true;
            }
        }

        false
    }

    /// 從消息中提取 XML 工具調用
    pub fn extract_xml_tool_calls(&self) -> Vec<ChatToolCall> {
        XmlToolCallParser::parse_xml_tool_calls(&self.content)
    }

    /// 基於提供的工具定義從消息中提取 XML 工具調用
    pub fn extract_xml_tool_calls_with_tools(&self, tools: &[ChatTool]) -> Vec<ChatToolCall> {
        XmlToolCallParser::parse_xml_tool_calls_with_tools(&self.content, tools)
    }
}