style without other explanations.\nOutput is separated only by commas.\nBreak down into at least 4-6 subproblems.\n\nOutput:",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "begin"
- ]
- },
- "Generate:RealLoopsVanish": {
- "downstream": [
- "Template:SpottyWaspsLose"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "In a detailed report — The report should focus on the answer to {IterationItem:OliveStatesSmoke}and nothing else.\n\n\nLanguage: {begin@language}\nContext as bellow: \n\n\"{Iteration:BlueClothsGrab}\"\n\nProvide the research report in the specified language, avoiding small talk.\nThe main content is provided in markdown format\nWrite all source urls at the end of the report in apa format. ",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "parent_id": "Iteration:ThreeParksChew",
- "upstream": [
- "IterationItem:OliveStatesSmoke"
- ]
- },
- "Generate:RedWormsDouble": {
- "downstream": [
- "Iteration:ThreeParksChew"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "According to query: ' {Generate:EveryCoinsStare}',for ' {begin@title}', generate 3 to 5 sub-titles.\n\n\nPlease generate 4 subheadings for the main title following these steps:\n - 1. Carefully read the provided main title and related content\n - 2. Analyze the core theme and key information points of the main title\n - 3. Ensure the generated subheadings maintain consistency and relevance with the main title\n - 4. Each subheading should:\n - Be concise and appropriate in length\n - Highlight a unique angle or key point\n - Capture readers' interest\n - Match the overall style and tone of the article\n - 5. Between subheadings:\n - Content should not overlap\n - Logical order should be maintained\n - Should collectively support the main title\n - Use numerical sequence (1, 2, 3...) to mark each subheading\n - 6. Output format requirements:\n - Each subheading on a separate line\n - No XML tags included\n - Output subheadings content only\n\n\nlanguage: {begin@language}\nGenerate a series of appropriate sub-title to help break down ' {begin@title}'.\nBreaks down complex topics into manageable subtopics.\n\nOutput:",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Generate:EveryCoinsStare"
- ]
- },
- "Generate:YoungClownsKnock": {
- "downstream": [],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "Your goal is to provide answers based on information from the internet. \nYou must use the provided search results to find relevant online information. \nYou should never use your own knowledge to answer questions.\nPlease include relevant url sources in the end of your answers.\n{Baidu:MeanBroomsMatter}\n\n\n\n\n\nlanguage: {begin@language}\n\n\n \" {Baidu:MeanBroomsMatter}\" \n\n\n\n\nUsing the above information, answer the following question or topic: \" {IterationItem:RudeTablesSmile} \"\nin a detailed report — The report should focus on the answer to the question, should be well structured, informative, in depth, with facts and numbers if available, a minimum of 1,200 words and with markdown syntax and apa format. Write all source urls at the end of the report in apa format. You should write your report only based on the given information and nothing else.",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "parent_id": "Iteration:BlueClothsGrab",
- "upstream": [
- "Baidu:MeanBroomsMatter"
- ]
- },
- "Iteration:BlueClothsGrab": {
- "downstream": [],
- "obj": {
- "component_name": "Iteration",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "delimiter": ",",
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "Generate:EveryCoinsStare",
- "type": "reference"
- }
- ]
- }
- },
- "upstream": [
- "Generate:EveryCoinsStare"
- ]
- },
- "Iteration:ThreeParksChew": {
- "downstream": [
- "Template:LegalDoorsAct"
- ],
- "obj": {
- "component_name": "Iteration",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "delimiter": "\n",
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "Generate:RedWormsDouble",
- "type": "reference"
- }
- ]
- }
- },
- "upstream": [
- "Generate:RedWormsDouble"
- ]
- },
- "IterationItem:OliveStatesSmoke": {
- "downstream": [
- "Generate:RealLoopsVanish"
- ],
- "obj": {
- "component_name": "IterationItem",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": []
- }
- },
- "parent_id": "Iteration:ThreeParksChew",
- "upstream": []
- },
- "IterationItem:RudeTablesSmile": {
- "downstream": [
- "Baidu:MeanBroomsMatter"
- ],
- "obj": {
- "component_name": "IterationItem",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": []
- }
- },
- "parent_id": "Iteration:BlueClothsGrab",
- "upstream": []
- },
- "Template:LegalDoorsAct": {
- "downstream": [
- "Answer:WittyBottlesJog"
- ],
- "obj": {
- "component_name": "Template",
- "inputs": [],
- "output": null,
- "params": {
- "content": " {begin@title}
\n\n\n\n{Iteration:ThreeParksChew}",
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "query": []
- }
- },
- "upstream": [
- "Iteration:ThreeParksChew"
- ]
- },
- "Template:SpottyWaspsLose": {
- "downstream": [],
- "obj": {
- "component_name": "Template",
- "inputs": [],
- "output": null,
- "params": {
- "content": " {IterationItem:OliveStatesSmoke}
\n {Generate:RealLoopsVanish}
",
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "query": []
- }
- },
- "parent_id": "Iteration:ThreeParksChew",
- "upstream": [
- "Generate:RealLoopsVanish"
- ]
- },
- "begin": {
- "downstream": [
- "Generate:EveryCoinsStare"
- ],
- "obj": {
- "component_name": "Begin",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "prologue": "",
- "query": [
- {
- "key": "title",
- "name": "Title",
- "optional": false,
- "type": "line"
- },
- {
- "key": "language",
- "name": "Language",
- "optional": false,
- "type": "line"
- }
- ]
- }
- },
- "upstream": []
- }
- },
- "embed_id": "",
- "graph": {
- "edges": [
- {
- "id": "reactflow__edge-Baidu:SharpHotelsNailb-Generate:RealCamerasSendb",
- "markerEnd": "logo",
- "source": "Baidu:SharpHotelsNail",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:RealCamerasSend",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "reactflow__edge-Generate:BeigeEyesFlyb-Template:ThinSnailsDreamc",
- "markerEnd": "logo",
- "source": "Generate:BeigeEyesFly",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Template:ThinSnailsDream",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "reactflow__edge-IterationItem:RudeTablesSmile-Baidu:MeanBroomsMatterc",
- "markerEnd": "logo",
- "source": "IterationItem:RudeTablesSmile",
- "sourceHandle": null,
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Baidu:MeanBroomsMatter",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:EveryCoinsStareb-Generate:RedWormsDoublec",
- "markerEnd": "logo",
- "source": "Generate:EveryCoinsStare",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:RedWormsDouble",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__begin-Generate:EveryCoinsStarec",
- "markerEnd": "logo",
- "source": "begin",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:EveryCoinsStare",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:RedWormsDoubleb-Iteration:ThreeParksChewc",
- "markerEnd": "logo",
- "source": "Generate:RedWormsDouble",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Iteration:ThreeParksChew",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:EveryCoinsStareb-Iteration:BlueClothsGrabc",
- "markerEnd": "logo",
- "source": "Generate:EveryCoinsStare",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Iteration:BlueClothsGrab",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Baidu:MeanBroomsMatterb-Generate:YoungClownsKnockb",
- "markerEnd": "logo",
- "source": "Baidu:MeanBroomsMatter",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:YoungClownsKnock",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__IterationItem:OliveStatesSmoke-Generate:RealLoopsVanishc",
- "markerEnd": "logo",
- "source": "IterationItem:OliveStatesSmoke",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:RealLoopsVanish",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:RealLoopsVanishb-Template:SpottyWaspsLoseb",
- "markerEnd": "logo",
- "source": "Generate:RealLoopsVanish",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Template:SpottyWaspsLose",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Iteration:ThreeParksChewb-Template:LegalDoorsActc",
- "markerEnd": "logo",
- "source": "Iteration:ThreeParksChew",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Template:LegalDoorsAct",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Template:LegalDoorsActb-Answer:WittyBottlesJogc",
- "markerEnd": "logo",
- "source": "Template:LegalDoorsAct",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:WittyBottlesJog",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- }
- ],
- "nodes": [
- {
- "data": {
- "form": {
- "prologue": "",
- "query": [
- {
- "key": "title",
- "name": "Title",
- "optional": false,
- "type": "line"
- },
- {
- "key": "language",
- "name": "Language",
- "optional": false,
- "type": "line"
- }
- ]
- },
- "label": "Begin",
- "name": "begin"
- },
- "dragging": false,
- "height": 130,
- "id": "begin",
- "measured": {
- "height": 130,
- "width": 200
- },
- "position": {
- "x": -231.29149905979648,
- "y": 95.28494230291383
- },
- "positionAbsolute": {
- "x": -185.67257819905137,
- "y": 108.15225637884839
- },
- "selected": false,
- "sourcePosition": "left",
- "targetPosition": "right",
- "type": "beginNode",
- "width": 200
- },
- {
- "data": {
- "form": {},
- "label": "Answer",
- "name": "Interact_0"
- },
- "dragging": false,
- "height": 44,
- "id": "Answer:WittyBottlesJog",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": 1458.2651570288865,
- "y": 164.22699667633927
- },
- "positionAbsolute": {
- "x": 1462.7745767525992,
- "y": 231.9248108743051
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "logicNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "delimiter": ",",
- "query": [
- {
- "component_id": "Generate:EveryCoinsStare",
- "type": "reference"
- }
- ]
- },
- "label": "Iteration",
- "name": "Search"
- },
- "dragging": false,
- "height": 192,
- "id": "Iteration:BlueClothsGrab",
- "measured": {
- "height": 192,
- "width": 334
- },
- "position": {
- "x": 432.63496522555613,
- "y": 228.82343789018051
- },
- "positionAbsolute": {
- "x": 441.29535207641436,
- "y": 291.9929929170084
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 337,
- "width": 356
- },
- "targetPosition": "left",
- "type": "group",
- "width": 334
- },
- {
- "data": {
- "form": {},
- "label": "IterationItem",
- "name": "IterationItem"
- },
- "dragging": false,
- "extent": "parent",
- "height": 44,
- "id": "IterationItem:RudeTablesSmile",
- "measured": {
- "height": 44,
- "width": 44
- },
- "parentId": "Iteration:BlueClothsGrab",
- "position": {
- "x": 22,
- "y": 10
- },
- "positionAbsolute": {
- "x": -261.5,
- "y": -288.14062500000006
- },
- "selected": false,
- "type": "iterationStartNode",
- "width": 44
- },
- {
- "data": {
- "form": {
- "query": [
- {
- "component_id": "IterationItem:RudeTablesSmile",
- "type": "reference"
- }
- ],
- "top_n": 10
- },
- "label": "Baidu",
- "name": "Baidu"
- },
- "dragging": false,
- "extent": "parent",
- "height": 64,
- "id": "Baidu:MeanBroomsMatter",
- "measured": {
- "height": 64,
- "width": 200
- },
- "parentId": "Iteration:BlueClothsGrab",
- "position": {
- "x": 200,
- "y": 0
- },
- "positionAbsolute": {
- "x": -83.49999999999999,
- "y": -298.14062500000006
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "delimiter": "\n",
- "query": [
- {
- "component_id": "Generate:RedWormsDouble",
- "type": "reference"
- }
- ]
- },
- "label": "Iteration",
- "name": "Sections"
- },
- "dragging": false,
- "height": 225,
- "id": "Iteration:ThreeParksChew",
- "measured": {
- "height": 225,
- "width": 315
- },
- "position": {
- "x": 888.9524716285371,
- "y": 75.91277516159235
- },
- "positionAbsolute": {
- "x": 891.9430519048244,
- "y": 39.64877134989487
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 438,
- "width": 328
- },
- "targetPosition": "left",
- "type": "group",
- "width": 315
- },
- {
- "data": {
- "form": {},
- "label": "IterationItem",
- "name": "IterationItem"
- },
- "dragging": false,
- "extent": "parent",
- "height": 44,
- "id": "IterationItem:OliveStatesSmoke",
- "measured": {
- "height": 44,
- "width": 44
- },
- "parentId": "Iteration:ThreeParksChew",
- "position": {
- "x": 24.66038685085823,
- "y": 37.00025154774299
- },
- "positionAbsolute": {
- "x": 780.5000000000002,
- "y": 432.859375
- },
- "selected": false,
- "type": "iterationStartNode",
- "width": 44
- },
- {
- "data": {
- "form": {
- "text": "It can generate a research report base on the title and language you provide."
- },
- "label": "Note",
- "name": "Usage"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 168,
- "id": "Note:PoorMirrorsJump",
- "measured": {
- "height": 168,
- "width": 275
- },
- "position": {
- "x": -192.4712202594548,
- "y": -164.26382748469516
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 275
- },
- {
- "data": {
- "form": {
- "text": "LLM provides a series of search engine queries related to the proposition. Comprehensive research can be conducted through queries from different perspectives."
- },
- "label": "Note",
- "name": "N-Query"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 207,
- "id": "Note:TwoSingersFly",
- "measured": {
- "height": 207,
- "width": 256
- },
- "position": {
- "x": 90.71637834539166,
- "y": -160.7863367019141
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 256
- },
- {
- "data": {
- "form": {
- "text": "LLM generates 4 subtitles for this report according to queries and title."
- },
- "label": "Note",
- "name": "N-Subtitles"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "id": "Note:SmoothAreasBet",
- "measured": {
- "height": 128,
- "width": 266
- },
- "position": {
- "x": 431.07789651000473,
- "y": -161.0756093374443
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode"
- },
- {
- "data": {
- "form": {
- "text": "LLM generates a report for each query based on search result of each query.\nYou could change Baidu to other search engines."
- },
- "label": "Note",
- "name": "N-Search"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 168,
- "id": "Note:CleanTablesCamp",
- "measured": {
- "height": 168,
- "width": 364
- },
- "position": {
- "x": 435.9578972976612,
- "y": 452.5021839330345
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 364
- },
- {
- "data": {
- "form": {
- "text": "LLM generates 4 sub-sections for 4 subtitles based on the report of search engine result."
- },
- "label": "Note",
- "name": "N-Sections"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 142,
- "id": "Note:FamousToesReply",
- "measured": {
- "height": 142,
- "width": 336
- },
- "position": {
- "x": 881.4352587545767,
- "y": -165.7333893115248
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 336
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "\n\nGenerate a series of appropriate search engine queries to break down questions based on user inquiries\n\n\n\n\nInput: User asks how to learn programming\nOutput: programming learning methods, programming tutorials for beginners\n\n\n\nInput: User wants to understand latest technology trends \nOutput: tech trends 2024, latest technology news\n\n\n\nInput: User seeks healthy eating advice\nOutput: healthy eating guide, balanced nutrition diet\n\n\n\n\n1. Take user's question as input.\n2. Identify relevant keywords or phrases based on the topic of user's question.\n3. Use these keywords or phrases to make search engine queries.\n4. Generate a series of appropriate search engine queries to help break down user's question.\n5. Ensure output content does not contain any xml tags.\n6. The output must be pure and conform to the style without other explanations.\n7. Break down into at least 4-6 subproblems.\n8. Output is separated only by commas.\n\n\n\ntitle: {begin@title}\nlanguage: {begin@language}\nThe output must be pure and conform to the style without other explanations.\nOutput is separated only by commas.\nBreak down into at least 4-6 subproblems.\n\nOutput:",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "GenQuery"
- },
- "dragging": false,
- "id": "Generate:EveryCoinsStare",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 42.60311386535324,
- "y": 107.45415912015176
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "According to query: ' {Generate:EveryCoinsStare}',for ' {begin@title}', generate 3 to 5 sub-titles.\n\n\nPlease generate 4 subheadings for the main title following these steps:\n - 1. Carefully read the provided main title and related content\n - 2. Analyze the core theme and key information points of the main title\n - 3. Ensure the generated subheadings maintain consistency and relevance with the main title\n - 4. Each subheading should:\n - Be concise and appropriate in length\n - Highlight a unique angle or key point\n - Capture readers' interest\n - Match the overall style and tone of the article\n - 5. Between subheadings:\n - Content should not overlap\n - Logical order should be maintained\n - Should collectively support the main title\n - Use numerical sequence (1, 2, 3...) to mark each subheading\n - 6. Output format requirements:\n - Each subheading on a separate line\n - No XML tags included\n - Output subheadings content only\n\n\nlanguage: {begin@language}\nGenerate a series of appropriate sub-title to help break down ' {begin@title}'.\nBreaks down complex topics into manageable subtopics.\n\nOutput:",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Subtitles"
- },
- "dragging": false,
- "id": "Generate:RedWormsDouble",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 433.41522248658606,
- "y": 14.302437349777136
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "Your goal is to provide answers based on information from the internet. \nYou must use the provided search results to find relevant online information. \nYou should never use your own knowledge to answer questions.\nPlease include relevant url sources in the end of your answers.\n{Baidu:MeanBroomsMatter}\n\n\n\n\n\nlanguage: {begin@language}\n\n\n \" {Baidu:MeanBroomsMatter}\" \n\n\n\n\nUsing the above information, answer the following question or topic: \" {IterationItem:RudeTablesSmile} \"\nin a detailed report — The report should focus on the answer to the question, should be well structured, informative, in depth, with facts and numbers if available, a minimum of 1,200 words and with markdown syntax and apa format. Write all source urls at the end of the report in apa format. You should write your report only based on the given information and nothing else.",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "GenSearchReport"
- },
- "dragging": false,
- "extent": "parent",
- "id": "Generate:YoungClownsKnock",
- "measured": {
- "height": 106,
- "width": 200
- },
- "parentId": "Iteration:BlueClothsGrab",
- "position": {
- "x": 115.34644687476163,
- "y": 73.07611243293042
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "In a detailed report — The report should focus on the answer to {IterationItem:OliveStatesSmoke}and nothing else.\n\n\nLanguage: {begin@language}\nContext as bellow: \n\n\"{Iteration:BlueClothsGrab}\"\n\nProvide the research report in the specified language, avoiding small talk.\nThe main content is provided in markdown format\nWrite all source urls at the end of the report in apa format. ",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Subtitle-content"
- },
- "dragging": false,
- "extent": "parent",
- "id": "Generate:RealLoopsVanish",
- "measured": {
- "height": 106,
- "width": 200
- },
- "parentId": "Iteration:ThreeParksChew",
- "position": {
- "x": 189.94391141062363,
- "y": 5.408501635610101
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "content": " {IterationItem:OliveStatesSmoke}
\n {Generate:RealLoopsVanish}
",
- "parameters": []
- },
- "label": "Template",
- "name": "Sub-section"
- },
- "dragging": false,
- "extent": "parent",
- "id": "Template:SpottyWaspsLose",
- "measured": {
- "height": 76,
- "width": 200
- },
- "parentId": "Iteration:ThreeParksChew",
- "position": {
- "x": 107.51010102435532,
- "y": 127.82322102671017
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "templateNode"
- },
- {
- "data": {
- "form": {
- "content": " {begin@title}
\n\n\n\n{Iteration:ThreeParksChew}",
- "parameters": []
- },
- "label": "Template",
- "name": "Article"
- },
- "dragging": false,
- "id": "Template:LegalDoorsAct",
- "measured": {
- "height": 76,
- "width": 200
- },
- "position": {
- "x": 1209.0758608851872,
- "y": 149.01984563839733
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "templateNode"
- }
- ]
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": []
- },
- "avatar": "data:image/jpeg;base64,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"
-}
\ No newline at end of file
diff --git a/agent/templates/seo_blog.json b/agent/templates/seo_blog.json
deleted file mode 100644
index 051d74642..000000000
--- a/agent/templates/seo_blog.json
+++ /dev/null
@@ -1,1209 +0,0 @@
-{
- "id": 9,
- "title": "SEO Blog Generator",
- "description": "A blog generator that creates SEO-optimized content based on your chosen title or keywords.",
- "canvas_type": "chatbot",
- "dsl": {
- "answer": [],
- "components": {
- "Answer:TameWavesChange": {
- "downstream": [],
- "obj": {
- "component_name": "Answer",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "post_answers": [],
- "query": []
- }
- },
- "upstream": [
- "Template:YellowPlumsYell"
- ]
- },
- "Baidu:SharpSignsBeg": {
- "downstream": [
- "Generate:FastTipsCamp"
- ],
- "obj": {
- "component_name": "Baidu",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "Generate:PublicPotsPush",
- "type": "reference"
- }
- ],
- "top_n": 10
- }
- },
- "upstream": [
- "Generate:PublicPotsPush"
- ]
- },
- "Baidu:ShyTeamsJuggle": {
- "downstream": [
- "Generate:ReadyHandsInvent"
- ],
- "obj": {
- "component_name": "Baidu",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "begin@keywords",
- "type": "reference"
- }
- ],
- "top_n": 10
- }
- },
- "upstream": [
- "Switch:LargeWaspsSlide"
- ]
- },
- "Generate:CuddlyBatsCamp": {
- "downstream": [
- "Template:YellowPlumsYell"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use positive, present tense expressions and avoid using complex words and sentence structures that lack narrative, such as \"reveal\" and \"dig deep.\"\n - Next, please continue writing articles related to our topic with a concise title, {begin@title}{Generate:ReadyHandsInvent} {begin@keywords}{Generate:FancyMomentsTalk}. \n - Please AVOID repeating what has already been written and do not use the same sentence structure. \n - JUST write the body of the article based on the outline.\n - DO NOT include introduction, title.\n - DO NOT miss anything mentioned in article outline, except introduction and title.\n - Please use the information I provide to create in-depth, interesting and unique content. Also, incorporate the references and data points I provided earlier into the article to increase its value to the reader.\n - MUST be in language as \" {begin@keywords} {begin@title}\".\n\n\n{Generate:FastTipsCamp}\n\n",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Generate:FastTipsCamp"
- ]
- },
- "Generate:FancyMomentsTalk": {
- "downstream": [
- "Generate:PublicPotsPush"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 256,
- "message_history_window_size": 12,
- "output": null,
- "output_var_name": "output",
- "parameters": [
- {
- "component_id": "begin@title",
- "id": "2beef84b-204b-475a-89b3-3833bd108088",
- "key": "title"
- }
- ],
- "presence_penalty": 0.4,
- "prompt": "I'm doing research for an article called {begin@title}, what relevant, high-traffic phrase should I type into Google to find this article? Just return the phrase without including any special symbols like quotes and colons.",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Switch:LargeWaspsSlide"
- ]
- },
- "Generate:FastTipsCamp": {
- "downstream": [
- "Generate:FortyBirdsAsk",
- "Generate:CuddlyBatsCamp"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "I'm an expert blogger.\nHere is some research I did for the blog post title \" {begin@title} {Generate:ReadyHandsInvent}\".\nThese are related search results:\n{Baidu:SharpSignsBeg}\n\nPlease study it in depth:\n\nArticle title: {begin@title} {Generate:ReadyHandsInvent}\nTarget keywords: {begin@keywords} {Generate:FancyMomentsTalk}\nMy blog post’s audience: {begin@audience}\nExclude brands: {begin@brands_to_avoid}\n\nCan you write a detailed blog outline with unique chapters? \n - The outline should include specific points and details that the article can mention. \n - AVOID generalities. \n - This SHOULD be researched in depth, not generalized.\n - Each chapter includes 7-8 projects, use some of the links above for reference if you can. For each item, don't just say \"discuss how\" but actually explain in detail the points that can be made. \n - DO NOT include things that you know are false and may contain inaccuracies. You are writing for a mature audience, avoid generalities and make specific quotes. Make sure to define key terms for users in your outline. Stay away from very controversial topics. \n - In the introduction, provide the background information needed for the rest of the article.\n - Please return in base array format and only the outline array, escaping quotes in the format. Each array item includes a complete chapter:\n[\"Includes Chapter 1 of all sub-projects\", \"Includes Chapter 2 of all sub-projects\", \"Includes Chapter 3 of all sub-projects\", \"Includes Chapter 4 of all sub-projects\"...etc.]\n - Each section SHOULD be wrapped with \"\" and ensure escaping within the content to ensure it is a valid array item.\n - MUST be in language of \" {begin@keywords} {begin@title}\".\n\nHere is an example of valid output. Please follow this structure and ignore the content:\n[\n \"Introduction - Explore the vibrant city of Miami, a destination that offers rich history, diverse culture, and many hidden treasures. Discover the little-known wonders that make Miami a unique destination for adventure seekers. Explore from historical landmarks to exotic places Attractions include atmospheric neighborhoods, local cuisine and lively nightlife. \",\n \"History of Miami - Begin the adventure with a journey into Miami's past. Learn about the city's transformation from a sleepy settlement to a busy metropolis. Understand the impact of multiculturalism on the city's development, as reflected in its architecture, cuisine and lifestyle See. Discover the historical significance of Miami landmarks like Hemingway's home. Uncover the fascinating stories of famous Miami neighborhoods like Key West. Explore the role of art and culture in shaping Miami, as shown at Art Basel events.\n\"Major Attractions - Go beyond Miami's famous beaches and explore the city's top attractions. Discover the artistic talent of the Wynwood Arts District, known for its vibrant street art. Visit iconic South Beach, known for its nightlife and boutiques . Explore the charming Coconut Grove district, known for its tree-lined streets and shopping areas. Visit the Holocaust Memorial Museum, a sombre reminder of a dark chapter in human history. Explore the Everglades Country, one of Miami's natural treasures. The park's diverse wildlife \",\n\"Trail Discovery - Get off the tourist trail and discover Miami's hidden treasures. Experience a water taxi tour across Biscayne Bay to get another perspective on the city. Visit the little-known Kabinett Department of Art, showcasing unique installation art . Explore the abandoned bridges and hidden bars of Duval Street and go on a culinary adventure in local neighborhoods known for their authentic cuisine. Go shopping at Brickell City Center, a trendy shopping and apartment complex in the heart of Miami. body.\",\n\"Local Cuisine - Dive into Miami's food scene and sample the city's diverse flavors. Enjoy ultra-fresh food and drinks at Bartaco, a local favorite. Experience fine dining at upscale Italian restaurants like Il Mulino New York. Explore the city ’s local food market and sample delicious local produce in Miami. Try a unique blend of Cuban and American cuisine that is a testament to Miami’s multicultural heritage.\"\n\"Nightlife - Experience the city's lively nightlife, a perfect blend of sophistication and fun. Visit America's Social Bar & Kitchen, a sports\nA hotspot for enthusiasts. Explore the nightlife of Mary Brickell Village, known for its clubby atmosphere. Spend an evening at Smith & Walensky Miami Beach's South Point Park, known for its stunning views and vintage wines. Visit iconic Miami Beach, famous for its pulsating nightlife. \",\n \"Conclusion- Miami is more than just stunning beaches and dazzling nightlife. It is a treasure trove of experiences waiting to be discovered. From its rich history and diverse culture to its hidden treasures, local cuisine and lively nightlife, Miami has something for everyone A traveler offers a unique adventure to experience the magic of Miami Beach and create unforgettable memories with your family.\"\n]",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Baidu:SharpSignsBeg"
- ]
- },
- "Generate:FortyBirdsAsk": {
- "downstream": [
- "Template:YellowPlumsYell"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use active, present tense, avoid using complex language and syntax, such as \"unravel\", \"dig deeper\", etc., \n - DO NOT provide narration.\n - Now, excluding the title, introduce the blog in 3-5 sentences. \n - Use h2 headings to write chapter titles. \n - Provide a concise, SEO-optimized title. \n - DO NOT include h3 subheadings. \n - Feel free to use bullet points, numbered lists or paragraphs, or bold text for emphasis when appropriate. \n - You should transition naturally to each section, build on each section, and should NOT repeat the same sentence structure. \n - JUST write the introduction of the article based on the outline.\n - DO NOT include title, conclusions, summaries, or summaries, no \"summaries,\" \"conclusions,\" or variations. \n - DO NOT include links or mention any companies that compete with the brand (avoid mentioning {begin@brands_to_avoid}).\n - JUST write the introduction of the article based on the outline.\n - MUST be in language as \"{Generate:FancyMomentsTalk} {Generate:ReadyHandsInvent}\".\n\n\n{Generate:FastTipsCamp}\n\n\n",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Generate:FastTipsCamp"
- ]
- },
- "Generate:PublicPotsPush": {
- "downstream": [
- "Baidu:SharpSignsBeg"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 256,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "I want a Google search phrase to get authoritative information for my article \" {begin@title} {Generate:ReadyHandsInvent} {begin@keywords} {Generate:FancyMomentsTalk}\" for {begin@audience}. Please return a search phrase of five words or less so that I can get a good overview of the topic. Include any words you're unfamiliar with in your search query.",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Generate:ReadyHandsInvent",
- "Generate:FancyMomentsTalk"
- ]
- },
- "Generate:ReadyHandsInvent": {
- "downstream": [
- "Generate:PublicPotsPush"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 256,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "Role: You are an SEO expert and subject area expert. Your task is to generate an SEO article title based on the keywords provided by the user and the context of the Google search.\n\nThe context of the Google search is as follows:\n{Baidu:ShyTeamsJuggle}\nThe context of the Google search is as above.\n\nIn order to craft an SEO article title that is keyword friendly and aligns with the principles observed in the top results you share, it is important to understand why these titles are effective. Here are the principles that may help them rank high:\n1. **Keyword Placement and Clarity**: Each title directly responds to the query by containing the exact keyword or a very close variation. This clarity ensures that search engines can easily understand the relevance of the content.\n2. **Succinctness and directness**: The title is concise, making it easy to read and understand quickly. They avoid unnecessary words and get straight to the point.\n3. **Contains a definition or explanation**: The title implies that the article will define or explain the concept, which is what people searching for \"{Generate:FancyMomentsTalk}\" are looking for.\n4. **Variety of Presentation**: Despite covering similar content, each title approaches the topic from a slightly different angle. This diversity can attract the interest of a wider audience.\n\nGiven these principles, please help me generate a title that will be optimized for the keyword \"{Generate:FancyMomentsTalk}\" based on the syntax of a top-ranking title. \n\nPlease don't copy, but give better options, and avoid using language like \"master,\" \"comprehensive,\" \"discover,\" or \"reveal.\" \n\nDo not use gerunds, only active tense and present tense. \n\nTitle SHOULD be in language as \"{Generate:FancyMomentsTalk}\"\n\nJust return the title.",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Baidu:ShyTeamsJuggle"
- ]
- },
- "Switch:LargeWaspsSlide": {
- "downstream": [
- "Baidu:ShyTeamsJuggle",
- "Generate:FancyMomentsTalk"
- ],
- "obj": {
- "component_name": "Switch",
- "inputs": [],
- "output": null,
- "params": {
- "conditions": [
- {
- "items": [
- {
- "cpn_id": "begin@title",
- "operator": "empty"
- }
- ],
- "logical_operator": "and",
- "to": "Baidu:ShyTeamsJuggle"
- }
- ],
- "debug_inputs": [],
- "end_cpn_id": "Generate:FancyMomentsTalk",
- "inputs": [],
- "message_history_window_size": 22,
- "operators": [
- "contains",
- "not contains",
- "start with",
- "end with",
- "empty",
- "not empty",
- "=",
- "≠",
- ">",
- "<",
- "≥",
- "≤"
- ],
- "output": null,
- "output_var_name": "output",
- "query": []
- }
- },
- "upstream": [
- "begin"
- ]
- },
- "Template:YellowPlumsYell": {
- "downstream": [
- "Answer:TameWavesChange"
- ],
- "obj": {
- "component_name": "Template",
- "inputs": [],
- "output": null,
- "params": {
- "content": "\n##{begin@title}{Generate:ReadyHandsInvent}\n\n{Generate:FortyBirdsAsk}\n\n\n\n{Generate:CuddlyBatsCamp}\n\n\n",
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "query": []
- }
- },
- "upstream": [
- "Generate:FortyBirdsAsk",
- "Generate:CuddlyBatsCamp"
- ]
- },
- "begin": {
- "downstream": [
- "Switch:LargeWaspsSlide"
- ],
- "obj": {
- "component_name": "Begin",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "prologue": "",
- "query": [
- {
- "key": "title",
- "name": "Title",
- "optional": true,
- "type": "line"
- },
- {
- "key": "keywords",
- "name": "Keywords",
- "optional": true,
- "type": "line"
- },
- {
- "key": "audience",
- "name": "Audience",
- "optional": true,
- "type": "line"
- },
- {
- "key": "brands_to_avoid",
- "name": "Brands to avoid",
- "optional": true,
- "type": "line"
- }
- ]
- }
- },
- "upstream": []
- }
- },
- "embed_id": "",
- "graph": {
- "edges": [
- {
- "id": "reactflow__edge-begin-Switch:LargeWaspsSlidea",
- "markerEnd": "logo",
- "source": "begin",
- "sourceHandle": null,
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Switch:LargeWaspsSlide",
- "targetHandle": "a",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Switch:LargeWaspsSlideCase 1-Baidu:ShyTeamsJugglec",
- "markerEnd": "logo",
- "source": "Switch:LargeWaspsSlide",
- "sourceHandle": "Case 1",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Baidu:ShyTeamsJuggle",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Switch:LargeWaspsSlideend_cpn_id-Generate:FancyMomentsTalkc",
- "markerEnd": "logo",
- "source": "Switch:LargeWaspsSlide",
- "sourceHandle": "end_cpn_id",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:FancyMomentsTalk",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "xy-edge__Baidu:ShyTeamsJuggleb-Generate:ReadyHandsInventc",
- "markerEnd": "logo",
- "source": "Baidu:ShyTeamsJuggle",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:ReadyHandsInvent",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:ReadyHandsInventb-Generate:PublicPotsPushc",
- "markerEnd": "logo",
- "source": "Generate:ReadyHandsInvent",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:PublicPotsPush",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:FancyMomentsTalkb-Generate:PublicPotsPushc",
- "markerEnd": "logo",
- "source": "Generate:FancyMomentsTalk",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:PublicPotsPush",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:PublicPotsPushb-Baidu:SharpSignsBegc",
- "markerEnd": "logo",
- "source": "Generate:PublicPotsPush",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Baidu:SharpSignsBeg",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Baidu:SharpSignsBegb-Generate:FastTipsCampc",
- "markerEnd": "logo",
- "source": "Baidu:SharpSignsBeg",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:FastTipsCamp",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:FastTipsCampb-Generate:FortyBirdsAskc",
- "markerEnd": "logo",
- "source": "Generate:FastTipsCamp",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:FortyBirdsAsk",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:FastTipsCampb-Generate:CuddlyBatsCampc",
- "markerEnd": "logo",
- "source": "Generate:FastTipsCamp",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:CuddlyBatsCamp",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:FortyBirdsAskb-Template:YellowPlumsYellc",
- "markerEnd": "logo",
- "source": "Generate:FortyBirdsAsk",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Template:YellowPlumsYell",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:CuddlyBatsCampb-Template:YellowPlumsYellc",
- "markerEnd": "logo",
- "source": "Generate:CuddlyBatsCamp",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Template:YellowPlumsYell",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Template:YellowPlumsYellb-Answer:TameWavesChangec",
- "markerEnd": "logo",
- "source": "Template:YellowPlumsYell",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:TameWavesChange",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- }
- ],
- "nodes": [
- {
- "data": {
- "form": {
- "prologue": "",
- "query": [
- {
- "key": "title",
- "name": "Title",
- "optional": true,
- "type": "line"
- },
- {
- "key": "keywords",
- "name": "Keywords",
- "optional": true,
- "type": "line"
- },
- {
- "key": "audience",
- "name": "Audience",
- "optional": true,
- "type": "line"
- },
- {
- "key": "brands_to_avoid",
- "name": "Brands to avoid",
- "optional": true,
- "type": "line"
- }
- ]
- },
- "label": "Begin",
- "name": "begin"
- },
- "dragging": false,
- "height": 212,
- "id": "begin",
- "measured": {
- "height": 212,
- "width": 200
- },
- "position": {
- "x": -432.2850120660528,
- "y": 82.47567395502324
- },
- "positionAbsolute": {
- "x": -432.2850120660528,
- "y": 82.47567395502324
- },
- "selected": false,
- "sourcePosition": "left",
- "targetPosition": "right",
- "type": "beginNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "conditions": [
- {
- "items": [
- {
- "cpn_id": "begin@title",
- "operator": "empty"
- }
- ],
- "logical_operator": "and",
- "to": "Baidu:ShyTeamsJuggle"
- }
- ],
- "end_cpn_id": "Generate:FancyMomentsTalk"
- },
- "label": "Switch",
- "name": "Empty title?"
- },
- "dragging": false,
- "height": 164,
- "id": "Switch:LargeWaspsSlide",
- "measured": {
- "height": 164,
- "width": 200
- },
- "position": {
- "x": -171.8139076194234,
- "y": 106.58178484885428
- },
- "positionAbsolute": {
- "x": -171.8139076194234,
- "y": 106.58178484885428
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "switchNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "query": [
- {
- "component_id": "begin@keywords",
- "type": "reference"
- }
- ],
- "top_n": 10
- },
- "label": "Baidu",
- "name": "Baidu4title"
- },
- "dragging": false,
- "height": 64,
- "id": "Baidu:ShyTeamsJuggle",
- "measured": {
- "height": 64,
- "width": 200
- },
- "position": {
- "x": 99.2698941117485,
- "y": 131.97513574677558
- },
- "positionAbsolute": {
- "x": 99.2698941117485,
- "y": 131.97513574677558
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 12,
- "parameter": "Precise",
- "parameters": [
- {
- "component_id": "begin@title",
- "id": "2beef84b-204b-475a-89b3-3833bd108088",
- "key": "title"
- }
- ],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "I'm doing research for an article called {begin@title}, what relevant, high-traffic phrase should I type into Google to find this article? Just return the phrase without including any special symbols like quotes and colons.",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Keywords gen"
- },
- "dragging": false,
- "height": 148,
- "id": "Generate:FancyMomentsTalk",
- "measured": {
- "height": 148,
- "width": 200
- },
- "position": {
- "x": 102.41401952481024,
- "y": 250.74278147746412
- },
- "positionAbsolute": {
- "x": 102.41401952481024,
- "y": 250.74278147746412
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "query": [
- {
- "component_id": "Generate:PublicPotsPush",
- "type": "reference"
- }
- ],
- "top_n": 10
- },
- "label": "Baidu",
- "name": "Baidu4Info"
- },
- "dragging": false,
- "height": 64,
- "id": "Baidu:SharpSignsBeg",
- "measured": {
- "height": 64,
- "width": 200
- },
- "position": {
- "x": 932.3075370153801,
- "y": 293.31101119905543
- },
- "positionAbsolute": {
- "x": 933.5156264729844,
- "y": 289.6867428262425
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {},
- "label": "Answer",
- "name": "Interact_0"
- },
- "dragging": false,
- "height": 44,
- "id": "Answer:TameWavesChange",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": 2067.9179213988796,
- "y": 373.3415280349531
- },
- "positionAbsolute": {
- "x": 2150.301454782809,
- "y": 360.9062777128506
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "logicNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "text": "Function: Collect information such as keywords, titles, audience, words/brands to avoid, tone, and other details provided by the user.\n\nVariables:\n - keyword:Keywords\n - title:Title, \n - audience:Audience\n - brands_to_avoid:Words/brands to avoid.\n\nMUST NOT both of keywords and title are blank."
- },
- "label": "Note",
- "name": "N:Begin"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 368,
- "id": "Note:FruityColtsBattle",
- "measured": {
- "height": 368,
- "width": 275
- },
- "position": {
- "x": -430.17115299591364,
- "y": -320.31044749815453
- },
- "positionAbsolute": {
- "x": -430.17115299591364,
- "y": -320.31044749815453
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 368,
- "width": 275
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 275
- },
- {
- "data": {
- "form": {
- "text": "If title is not empty, let LLM help you to generate keywords."
- },
- "label": "Note",
- "name": "N: Keywords gen"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 128,
- "id": "Note:SilverGiftsHide",
- "measured": {
- "height": 128,
- "width": 269
- },
- "position": {
- "x": 100.4673650631783,
- "y": 414.8198461927788
- },
- "positionAbsolute": {
- "x": 100.4673650631783,
- "y": 414.8198461927788
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 269
- },
- {
- "data": {
- "form": {
- "text": "Use user defined keywords to search.\nNext, generate a title based on the search result.\nChange to DuckDuckGo if you want."
- },
- "label": "Note",
- "name": "N: Baidu4title"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 192,
- "id": "Note:ShaggyMelonsFail",
- "measured": {
- "height": 192,
- "width": 254
- },
- "position": {
- "x": 101.98068917850298,
- "y": -79.85480052081127
- },
- "positionAbsolute": {
- "x": 101.98068917850298,
- "y": -79.85480052081127
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 192,
- "width": 254
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 254
- },
- {
- "data": {
- "form": {
- "text": "Let LLM to generate keywords to search. \nBased on the search result, the outline of the article will be generated."
- },
- "label": "Note",
- "name": "N: Words to search"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 132,
- "id": "Note:EvilIdeasDress",
- "measured": {
- "height": 132,
- "width": 496
- },
- "position": {
- "x": 822.1382301557384,
- "y": 1.1013324480075255
- },
- "positionAbsolute": {
- "x": 822.1382301557384,
- "y": 1.1013324480075255
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 132,
- "width": 496
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 496
- },
- {
- "data": {
- "form": {
- "text": "1 . User input:\nThe user enters information such as avoid keywords, title, audience, required words/brands, tone, etc. at the start node.\n\n2. Conditional judgment:\nCheck whether the title is empty, if it is empty, generate the title.\n\n3. Generate titles and keywords:\nGenerate SEO optimized titles and related keywords based on the entered user keywords.\n\n4. Web search:\nUse the generated titles and keywords to conduct a Google search to obtain relevant information.\n\n5. Generate outline and articles:\nGenerate article outlines, topics, and bodies based on user input information and search results.\n\n6. Template conversion and output:\nCombine the beginning of the article and the main body to generate a complete article, and output the result."
- },
- "label": "Note",
- "name": "Steps"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 456,
- "id": "Note:WeakApesDivide",
- "measured": {
- "height": 456,
- "width": 955
- },
- "position": {
- "x": 441.5385839522079,
- "y": 638.4606789293297
- },
- "positionAbsolute": {
- "x": 377.5385839522079,
- "y": 638.4606789293297
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 450,
- "width": 827
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 955
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "Role: You are an SEO expert and subject area expert. Your task is to generate an SEO article title based on the keywords provided by the user and the context of the Google search.\n\nThe context of the Google search is as follows:\n{Baidu:ShyTeamsJuggle}\nThe context of the Google search is as above.\n\nIn order to craft an SEO article title that is keyword friendly and aligns with the principles observed in the top results you share, it is important to understand why these titles are effective. Here are the principles that may help them rank high:\n1. **Keyword Placement and Clarity**: Each title directly responds to the query by containing the exact keyword or a very close variation. This clarity ensures that search engines can easily understand the relevance of the content.\n2. **Succinctness and directness**: The title is concise, making it easy to read and understand quickly. They avoid unnecessary words and get straight to the point.\n3. **Contains a definition or explanation**: The title implies that the article will define or explain the concept, which is what people searching for \"{Generate:FancyMomentsTalk}\" are looking for.\n4. **Variety of Presentation**: Despite covering similar content, each title approaches the topic from a slightly different angle. This diversity can attract the interest of a wider audience.\n\nGiven these principles, please help me generate a title that will be optimized for the keyword \"{Generate:FancyMomentsTalk}\" based on the syntax of a top-ranking title. \n\nPlease don't copy, but give better options, and avoid using language like \"master,\" \"comprehensive,\" \"discover,\" or \"reveal.\" \n\nDo not use gerunds, only active tense and present tense. \n\nTitle SHOULD be in language as \"{Generate:FancyMomentsTalk}\"\n\nJust return the title.",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Title Gen"
- },
- "dragging": false,
- "id": "Generate:ReadyHandsInvent",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 362.61841535531624,
- "y": 109.52633857873508
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "I want a Google search phrase to get authoritative information for my article \" {begin@title} {Generate:ReadyHandsInvent} {begin@keywords} {Generate:FancyMomentsTalk}\" for {begin@audience}. Please return a search phrase of five words or less so that I can get a good overview of the topic. Include any words you're unfamiliar with in your search query.",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Words to search"
- },
- "dragging": false,
- "id": "Generate:PublicPotsPush",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 631.7110159663526,
- "y": 271.70568678331114
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "I'm an expert blogger.\nHere is some research I did for the blog post title \" {begin@title} {Generate:ReadyHandsInvent}\".\nThese are related search results:\n{Baidu:SharpSignsBeg}\n\nPlease study it in depth:\n\nArticle title: {begin@title} {Generate:ReadyHandsInvent}\nTarget keywords: {begin@keywords} {Generate:FancyMomentsTalk}\nMy blog post’s audience: {begin@audience}\nExclude brands: {begin@brands_to_avoid}\n\nCan you write a detailed blog outline with unique chapters? \n - The outline should include specific points and details that the article can mention. \n - AVOID generalities. \n - This SHOULD be researched in depth, not generalized.\n - Each chapter includes 7-8 projects, use some of the links above for reference if you can. For each item, don't just say \"discuss how\" but actually explain in detail the points that can be made. \n - DO NOT include things that you know are false and may contain inaccuracies. You are writing for a mature audience, avoid generalities and make specific quotes. Make sure to define key terms for users in your outline. Stay away from very controversial topics. \n - In the introduction, provide the background information needed for the rest of the article.\n - Please return in base array format and only the outline array, escaping quotes in the format. Each array item includes a complete chapter:\n[\"Includes Chapter 1 of all sub-projects\", \"Includes Chapter 2 of all sub-projects\", \"Includes Chapter 3 of all sub-projects\", \"Includes Chapter 4 of all sub-projects\"...etc.]\n - Each section SHOULD be wrapped with \"\" and ensure escaping within the content to ensure it is a valid array item.\n - MUST be in language of \" {begin@keywords} {begin@title}\".\n\nHere is an example of valid output. Please follow this structure and ignore the content:\n[\n \"Introduction - Explore the vibrant city of Miami, a destination that offers rich history, diverse culture, and many hidden treasures. Discover the little-known wonders that make Miami a unique destination for adventure seekers. Explore from historical landmarks to exotic places Attractions include atmospheric neighborhoods, local cuisine and lively nightlife. \",\n \"History of Miami - Begin the adventure with a journey into Miami's past. Learn about the city's transformation from a sleepy settlement to a busy metropolis. Understand the impact of multiculturalism on the city's development, as reflected in its architecture, cuisine and lifestyle See. Discover the historical significance of Miami landmarks like Hemingway's home. Uncover the fascinating stories of famous Miami neighborhoods like Key West. Explore the role of art and culture in shaping Miami, as shown at Art Basel events.\n\"Major Attractions - Go beyond Miami's famous beaches and explore the city's top attractions. Discover the artistic talent of the Wynwood Arts District, known for its vibrant street art. Visit iconic South Beach, known for its nightlife and boutiques . Explore the charming Coconut Grove district, known for its tree-lined streets and shopping areas. Visit the Holocaust Memorial Museum, a sombre reminder of a dark chapter in human history. Explore the Everglades Country, one of Miami's natural treasures. The park's diverse wildlife \",\n\"Trail Discovery - Get off the tourist trail and discover Miami's hidden treasures. Experience a water taxi tour across Biscayne Bay to get another perspective on the city. Visit the little-known Kabinett Department of Art, showcasing unique installation art . Explore the abandoned bridges and hidden bars of Duval Street and go on a culinary adventure in local neighborhoods known for their authentic cuisine. Go shopping at Brickell City Center, a trendy shopping and apartment complex in the heart of Miami. body.\",\n\"Local Cuisine - Dive into Miami's food scene and sample the city's diverse flavors. Enjoy ultra-fresh food and drinks at Bartaco, a local favorite. Experience fine dining at upscale Italian restaurants like Il Mulino New York. Explore the city ’s local food market and sample delicious local produce in Miami. Try a unique blend of Cuban and American cuisine that is a testament to Miami’s multicultural heritage.\"\n\"Nightlife - Experience the city's lively nightlife, a perfect blend of sophistication and fun. Visit America's Social Bar & Kitchen, a sports\nA hotspot for enthusiasts. Explore the nightlife of Mary Brickell Village, known for its clubby atmosphere. Spend an evening at Smith & Walensky Miami Beach's South Point Park, known for its stunning views and vintage wines. Visit iconic Miami Beach, famous for its pulsating nightlife. \",\n \"Conclusion- Miami is more than just stunning beaches and dazzling nightlife. It is a treasure trove of experiences waiting to be discovered. From its rich history and diverse culture to its hidden treasures, local cuisine and lively nightlife, Miami has something for everyone A traveler offers a unique adventure to experience the magic of Miami Beach and create unforgettable memories with your family.\"\n]",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Outline gen"
- },
- "dragging": false,
- "id": "Generate:FastTipsCamp",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 1188.847302971411,
- "y": 272.42758089250634
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use active, present tense, avoid using complex language and syntax, such as \"unravel\", \"dig deeper\", etc., \n - DO NOT provide narration.\n - Now, excluding the title, introduce the blog in 3-5 sentences. \n - Use h2 headings to write chapter titles. \n - Provide a concise, SEO-optimized title. \n - DO NOT include h3 subheadings. \n - Feel free to use bullet points, numbered lists or paragraphs, or bold text for emphasis when appropriate. \n - You should transition naturally to each section, build on each section, and should NOT repeat the same sentence structure. \n - JUST write the introduction of the article based on the outline.\n - DO NOT include title, conclusions, summaries, or summaries, no \"summaries,\" \"conclusions,\" or variations. \n - DO NOT include links or mention any companies that compete with the brand (avoid mentioning {begin@brands_to_avoid}).\n - JUST write the introduction of the article based on the outline.\n - MUST be in language as \"{Generate:FancyMomentsTalk} {Generate:ReadyHandsInvent}\".\n\n\n{Generate:FastTipsCamp}\n\n\n",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Introduction gen"
- },
- "dragging": false,
- "id": "Generate:FortyBirdsAsk",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 1467.1832072218494,
- "y": 273.6641444369902
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use positive, present tense expressions and avoid using complex words and sentence structures that lack narrative, such as \"reveal\" and \"dig deep.\"\n - Next, please continue writing articles related to our topic with a concise title, {begin@title}{Generate:ReadyHandsInvent} {begin@keywords}{Generate:FancyMomentsTalk}. \n - Please AVOID repeating what has already been written and do not use the same sentence structure. \n - JUST write the body of the article based on the outline.\n - DO NOT include introduction, title.\n - DO NOT miss anything mentioned in article outline, except introduction and title.\n - Please use the information I provide to create in-depth, interesting and unique content. Also, incorporate the references and data points I provided earlier into the article to increase its value to the reader.\n - MUST be in language as \" {begin@keywords} {begin@title}\".\n\n\n{Generate:FastTipsCamp}\n\n",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "Body gen"
- },
- "dragging": false,
- "id": "Generate:CuddlyBatsCamp",
- "measured": {
- "height": 108,
- "width": 200
- },
- "position": {
- "x": 1459.030461505832,
- "y": 430.80927477654984
- },
- "selected": true,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- },
- {
- "data": {
- "form": {
- "content": "\n##{begin@title}{Generate:ReadyHandsInvent}\n\n{Generate:FortyBirdsAsk}\n\n\n\n{Generate:CuddlyBatsCamp}\n\n\n",
- "parameters": []
- },
- "label": "Template",
- "name": "Template trans"
- },
- "dragging": false,
- "id": "Template:YellowPlumsYell",
- "measured": {
- "height": 76,
- "width": 200
- },
- "position": {
- "x": 1784.1452214476085,
- "y": 356.5796437282643
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "templateNode"
- }
- ]
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": []
- },
- "avatar": "data:image/jpeg;base64,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"
-}
\ No newline at end of file
diff --git a/agent/templates/text2sql.json b/agent/templates/text2sql.json
deleted file mode 100644
index 9d59d3005..000000000
--- a/agent/templates/text2sql.json
+++ /dev/null
@@ -1,651 +0,0 @@
-{
- "id": 5,
- "title": "Text To SQL",
- "description": "An agent that converts user queries into SQL statements. You must prepare three knowledge bases: 1: DDL for your database; 2: Examples of user queries converted to SQL statements; 3: A comprehensive description of your database, including but not limited to tables and records.",
- "canvas_type": "chatbot",
- "dsl": {
- "answer": [],
- "components": {
- "Answer:SocialAdsWonder": {
- "downstream": [
- "Retrieval:TrueCornersJam",
- "Retrieval:EasyDryersShop",
- "Retrieval:LazyChefsWatch"
- ],
- "obj": {
- "component_name": "Answer",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "post_answers": [],
- "query": []
- }
- },
- "upstream": [
- "begin",
- "Generate:CurlyFalconsWorry"
- ]
- },
- "Generate:CurlyFalconsWorry": {
- "downstream": [
- "Answer:SocialAdsWonder"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": false,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 1,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:TrueCornersJam}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:LazyChefsWatch}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:EasyDryersShop}.\n\n",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Retrieval:LazyChefsWatch",
- "Retrieval:EasyDryersShop",
- "Retrieval:TrueCornersJam"
- ]
- },
- "Retrieval:EasyDryersShop": {
- "downstream": [
- "Generate:CurlyFalconsWorry"
- ],
- "obj": {
- "component_name": "Retrieval",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "empty_response": "Nothing found in Q-SQL!",
- "inputs": [],
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [],
- "rerank_id": "",
- "similarity_threshold": 0.2,
- "top_k": 1024,
- "top_n": 8
- }
- },
- "upstream": [
- "Answer:SocialAdsWonder"
- ]
- },
- "Retrieval:LazyChefsWatch": {
- "downstream": [
- "Generate:CurlyFalconsWorry"
- ],
- "obj": {
- "component_name": "Retrieval",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "empty_response": "Nothing found in DB-Description!",
- "inputs": [],
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [],
- "rerank_id": "",
- "similarity_threshold": 0.2,
- "top_k": 1024,
- "top_n": 8
- }
- },
- "upstream": [
- "Answer:SocialAdsWonder"
- ]
- },
- "Retrieval:TrueCornersJam": {
- "downstream": [
- "Generate:CurlyFalconsWorry"
- ],
- "obj": {
- "component_name": "Retrieval",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "empty_response": "Nothing found in DDL!",
- "inputs": [],
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [],
- "rerank_id": "",
- "similarity_threshold": 0.02,
- "top_k": 1024,
- "top_n": 8
- }
- },
- "upstream": [
- "Answer:SocialAdsWonder"
- ]
- },
- "begin": {
- "downstream": [
- "Answer:SocialAdsWonder"
- ],
- "obj": {
- "component_name": "Begin",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "prologue": "Hi! I'm your smart assistant. What can I do for you?",
- "query": []
- }
- },
- "upstream": []
- }
- },
- "embed_id": "",
- "graph": {
- "edges": [
- {
- "id": "reactflow__edge-begin-Answer:SocialAdsWonderc",
- "markerEnd": "logo",
- "source": "begin",
- "sourceHandle": null,
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:SocialAdsWonder",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:TrueCornersJamc",
- "markerEnd": "logo",
- "source": "Answer:SocialAdsWonder",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Retrieval:TrueCornersJam",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:EasyDryersShopc",
- "markerEnd": "logo",
- "source": "Answer:SocialAdsWonder",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Retrieval:EasyDryersShop",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:LazyChefsWatchc",
- "markerEnd": "logo",
- "source": "Answer:SocialAdsWonder",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Retrieval:LazyChefsWatch",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "xy-edge__Retrieval:LazyChefsWatchb-Generate:CurlyFalconsWorryb",
- "markerEnd": "logo",
- "source": "Retrieval:LazyChefsWatch",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:CurlyFalconsWorry",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Retrieval:EasyDryersShopb-Generate:CurlyFalconsWorryb",
- "markerEnd": "logo",
- "source": "Retrieval:EasyDryersShop",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:CurlyFalconsWorry",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Retrieval:TrueCornersJamb-Generate:CurlyFalconsWorryb",
- "markerEnd": "logo",
- "source": "Retrieval:TrueCornersJam",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:CurlyFalconsWorry",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Generate:CurlyFalconsWorryc-Answer:SocialAdsWonderc",
- "markerEnd": "logo",
- "source": "Generate:CurlyFalconsWorry",
- "sourceHandle": "c",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:SocialAdsWonder",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- }
- ],
- "nodes": [
- {
- "data": {
- "label": "Begin",
- "name": "begin"
- },
- "dragging": false,
- "height": 44,
- "id": "begin",
- "measured": {
- "height": 44,
- "width": 100
- },
- "position": {
- "x": -520.486587527275,
- "y": 117.87988995940702
- },
- "positionAbsolute": {
- "x": -520.486587527275,
- "y": 117.87988995940702
- },
- "selected": false,
- "sourcePosition": "left",
- "targetPosition": "right",
- "type": "beginNode"
- },
- {
- "data": {
- "form": {},
- "label": "Answer",
- "name": "interface"
- },
- "dragging": false,
- "height": 44,
- "id": "Answer:SocialAdsWonder",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": -237.69220760465112,
- "y": 119.9282206409824
- },
- "positionAbsolute": {
- "x": -284.9289105495367,
- "y": 119.9282206409824
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "logicNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "empty_response": "Nothing found in DDL!",
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "similarity_threshold": 0.02,
- "top_n": 8
- },
- "label": "Retrieval",
- "name": "DDL"
- },
- "dragging": false,
- "height": 44,
- "id": "Retrieval:TrueCornersJam",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": 119.61927071085717,
- "y": -40.184181873335746
- },
- "positionAbsolute": {
- "x": 119.61927071085717,
- "y": -40.184181873335746
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "retrievalNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "empty_response": "Nothing found in Q-SQL!",
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "similarity_threshold": 0.2,
- "top_n": 8
- },
- "label": "Retrieval",
- "name": "Q->SQL"
- },
- "dragging": false,
- "height": 44,
- "id": "Retrieval:EasyDryersShop",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": 80.07777425685605,
- "y": 120.03075150115158
- },
- "positionAbsolute": {
- "x": 81.2024576603057,
- "y": 94.16303322180948
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "retrievalNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "empty_response": "Nothing found in DB-Description!",
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "similarity_threshold": 0.2,
- "top_n": 8
- },
- "label": "Retrieval",
- "name": "DB Description"
- },
- "dragging": false,
- "height": 44,
- "id": "Retrieval:LazyChefsWatch",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": 51.228157704293324,
- "y": 252.77721891325103
- },
- "positionAbsolute": {
- "x": 51.228157704293324,
- "y": 252.77721891325103
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "retrievalNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "text": "Receives a sentence that the user wants to convert into SQL and displays the result of the large model's SQL conversion."
- },
- "label": "Note",
- "name": "N: Interface"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 132,
- "id": "Note:GentleRabbitsWonder",
- "measured": {
- "height": 132,
- "width": 324
- },
- "position": {
- "x": -287.3066094433631,
- "y": -30.808189185380513
- },
- "positionAbsolute": {
- "x": -287.3066094433631,
- "y": -30.808189185380513
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 132,
- "width": 324
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 324
- },
- {
- "data": {
- "form": {
- "text": "The large model learns which tables may be available based on the responses from three knowledge bases and converts the user's input into SQL statements."
- },
- "label": "Note",
- "name": "N: LLM"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 163,
- "id": "Note:SixCitiesJoke",
- "measured": {
- "height": 163,
- "width": 334
- },
- "position": {
- "x": 19.243366453487255,
- "y": 531.9336820600888
- },
- "positionAbsolute": {
- "x": 5.12121582244032,
- "y": 637.6539219843564
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 147,
- "width": 326
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 334
- },
- {
- "data": {
- "form": {
- "text": "Searches for description about meanings of tables and fields."
- },
- "label": "Note",
- "name": "N: DB description"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 128,
- "id": "Note:FamousCarpetsTaste",
- "measured": {
- "height": 128,
- "width": 269
- },
- "position": {
- "x": 399.9267065852242,
- "y": 250.0329701879931
- },
- "positionAbsolute": {
- "x": 399.9267065852242,
- "y": 250.0329701879931
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 269
- },
- {
- "data": {
- "form": {
- "text": "Searches for samples about question to SQL.\nPlease check this dataset: https://huggingface.co/datasets/InfiniFlow/text2sql"
- },
- "label": "Note",
- "name": "N: Q->SQL"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 140,
- "id": "Note:PoliteBeesArrive",
- "measured": {
- "height": 140,
- "width": 455
- },
- "position": {
- "x": 491.0393427986917,
- "y": 96.58232093146341
- },
- "positionAbsolute": {
- "x": 489.0393427986917,
- "y": 96.58232093146341
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 130,
- "width": 451
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 455
- },
- {
- "data": {
- "form": {
- "text": "DDL(Data Definition Language).\n\nSearches for relevant database creation statements.\n\nIt should bind with a KB to which DDL is dumped in.\nYou could use 'General' as parsing method and ';' as delimiter."
- },
- "label": "Note",
- "name": "N: DDL"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 272,
- "id": "Note:SmartWingsDouble",
- "measured": {
- "height": 272,
- "width": 288
- },
- "position": {
- "x": 406.6930553966363,
- "y": -208.84980249039137
- },
- "positionAbsolute": {
- "x": 404.1930553966363,
- "y": -208.84980249039137
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 258,
- "width": 283
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 288
- },
- {
- "data": {
- "form": {
- "cite": false,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 1,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:TrueCornersJam}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:LazyChefsWatch}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:EasyDryersShop}.\n\n",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "GenSQL"
- },
- "dragging": false,
- "id": "Generate:CurlyFalconsWorry",
- "measured": {
- "height": 106,
- "width": 200
- },
- "position": {
- "x": 10.728415797190792,
- "y": 410.2569651241076
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- }
- ]
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": []
- },
- "avatar": "data:image/png;base64,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"
-}
diff --git a/agent/templates/websearch_assistant.json b/agent/templates/websearch_assistant.json
deleted file mode 100644
index 1b6308f69..000000000
--- a/agent/templates/websearch_assistant.json
+++ /dev/null
@@ -1,996 +0,0 @@
-{
- "id": 0,
- "title": "WebSearch Assistant",
- "description": "A chat assistant template that integrates information extracted from a knowledge base and web searches to respond to queries. Let's begin by setting up your knowledge base in 'Retrieval'!",
- "canvas_type": "chatbot",
- "dsl": {
- "answer": [],
- "components": {
- "Answer:PoorMapsCover": {
- "downstream": [
- "RewriteQuestion:OrangeBottlesSwim"
- ],
- "obj": {
- "component_name": "Answer",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "post_answers": [],
- "query": []
- }
- },
- "upstream": [
- "begin",
- "Generate:ItchyRiversDrum"
- ]
- },
- "Baidu:OliveAreasCall": {
- "downstream": [
- "Generate:ItchyRiversDrum"
- ],
- "obj": {
- "component_name": "Baidu",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- }
- },
- "upstream": [
- "KeywordExtract:BeigeTipsStand"
- ]
- },
- "DuckDuckGo:SoftButtonsRefuse": {
- "downstream": [
- "Generate:ItchyRiversDrum"
- ],
- "obj": {
- "component_name": "DuckDuckGo",
- "inputs": [],
- "output": null,
- "params": {
- "channel": "text",
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- }
- },
- "upstream": [
- "KeywordExtract:BeigeTipsStand"
- ]
- },
- "Generate:ItchyRiversDrum": {
- "downstream": [
- "Answer:PoorMapsCover"
- ],
- "obj": {
- "component_name": "Generate",
- "inputs": [],
- "output": null,
- "params": {
- "cite": true,
- "debug_inputs": [],
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "max_tokens": 0,
- "message_history_window_size": 12,
- "output": null,
- "output_var_name": "output",
- "parameters": [],
- "presence_penalty": 0.4,
- "prompt": "Role: You are an intelligent assistant. \nTask: Chat with user. Answer the question based on the provided content from: Knowledge Base, Wikipedia, Duckduckgo, Baidu.\nRequirements:\n - Answer should be in markdown format.\n - Answer should include all sources(Knowledge Base, Wikipedia, Duckduckgo, Baidu) as long as they are relevant, and label the sources of the cited content separately.\n - Attach URL links to the content which is quoted from Wikipedia, DuckDuckGo or Baidu.\n - Do not make thing up when there's no relevant information to user's question. \n\n## Knowledge base content\n{Retrieval:SilentCamelsStick}\n\n\n## Wikipedia content\n{Wikipedia:WittyRiceLearn}\n\n\n## Duckduckgo content\n{DuckDuckGo:SoftButtonsRefuse}\n\n\n## Baidu content\n{Baidu:OliveAreasCall}\n\n",
- "query": [],
- "temperature": 0.1,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Retrieval:SilentCamelsStick",
- "Wikipedia:WittyRiceLearn",
- "Baidu:OliveAreasCall",
- "DuckDuckGo:SoftButtonsRefuse"
- ]
- },
- "KeywordExtract:BeigeTipsStand": {
- "downstream": [
- "Baidu:OliveAreasCall",
- "DuckDuckGo:SoftButtonsRefuse",
- "Wikipedia:WittyRiceLearn"
- ],
- "obj": {
- "component_name": "KeywordExtract",
- "inputs": [],
- "output": null,
- "params": {
- "cite": true,
- "debug_inputs": [],
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "",
- "query": [],
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_n": 2,
- "top_p": 0.3
- }
- },
- "upstream": [
- "RewriteQuestion:OrangeBottlesSwim"
- ]
- },
- "Retrieval:SilentCamelsStick": {
- "downstream": [
- "Generate:ItchyRiversDrum"
- ],
- "obj": {
- "component_name": "Retrieval",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "empty_response": "The answer you want was not found in the knowledge base!",
- "inputs": [],
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [],
- "rerank_id": "",
- "similarity_threshold": 0.2,
- "top_k": 1024,
- "top_n": 8
- }
- },
- "upstream": [
- "RewriteQuestion:OrangeBottlesSwim"
- ]
- },
- "RewriteQuestion:OrangeBottlesSwim": {
- "downstream": [
- "KeywordExtract:BeigeTipsStand",
- "Retrieval:SilentCamelsStick"
- ],
- "obj": {
- "component_name": "RewriteQuestion",
- "inputs": [],
- "output": null,
- "params": {
- "cite": true,
- "debug_inputs": [],
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "inputs": [],
- "llm_id": "deepseek-chat@DeepSeek",
- "loop": 1,
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 6,
- "output": null,
- "output_var_name": "output",
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "",
- "query": [],
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- }
- },
- "upstream": [
- "Answer:PoorMapsCover"
- ]
- },
- "Wikipedia:WittyRiceLearn": {
- "downstream": [
- "Generate:ItchyRiversDrum"
- ],
- "obj": {
- "component_name": "Wikipedia",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "language": "en",
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- }
- },
- "upstream": [
- "KeywordExtract:BeigeTipsStand"
- ]
- },
- "begin": {
- "downstream": [
- "Answer:PoorMapsCover"
- ],
- "obj": {
- "component_name": "Begin",
- "inputs": [],
- "output": null,
- "params": {
- "debug_inputs": [],
- "inputs": [],
- "message_history_window_size": 22,
- "output": null,
- "output_var_name": "output",
- "prologue": "Hi! I'm your smart assistant. What can I do for you?",
- "query": []
- }
- },
- "upstream": []
- }
- },
- "embed_id": "",
- "graph": {
- "edges": [
- {
- "id": "reactflow__edge-begin-Answer:PoorMapsCoverc",
- "markerEnd": "logo",
- "source": "begin",
- "sourceHandle": null,
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:PoorMapsCover",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-Answer:PoorMapsCoverb-RewriteQuestion:OrangeBottlesSwimc",
- "markerEnd": "logo",
- "source": "Answer:PoorMapsCover",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "RewriteQuestion:OrangeBottlesSwim",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-RewriteQuestion:OrangeBottlesSwimb-KeywordExtract:BeigeTipsStandc",
- "markerEnd": "logo",
- "source": "RewriteQuestion:OrangeBottlesSwim",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "KeywordExtract:BeigeTipsStand",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-Baidu:OliveAreasCallc",
- "markerEnd": "logo",
- "source": "KeywordExtract:BeigeTipsStand",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Baidu:OliveAreasCall",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-DuckDuckGo:SoftButtonsRefusec",
- "markerEnd": "logo",
- "source": "KeywordExtract:BeigeTipsStand",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "DuckDuckGo:SoftButtonsRefuse",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-Wikipedia:WittyRiceLearnc",
- "markerEnd": "logo",
- "source": "KeywordExtract:BeigeTipsStand",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Wikipedia:WittyRiceLearn",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "reactflow__edge-RewriteQuestion:OrangeBottlesSwimb-Retrieval:SilentCamelsStickc",
- "markerEnd": "logo",
- "source": "RewriteQuestion:OrangeBottlesSwim",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Retrieval:SilentCamelsStick",
- "targetHandle": "c",
- "type": "buttonEdge"
- },
- {
- "id": "xy-edge__Generate:ItchyRiversDrumc-Answer:PoorMapsCoverc",
- "markerEnd": "logo",
- "source": "Generate:ItchyRiversDrum",
- "sourceHandle": "c",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Answer:PoorMapsCover",
- "targetHandle": "c",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Retrieval:SilentCamelsStickb-Generate:ItchyRiversDrumb",
- "markerEnd": "logo",
- "source": "Retrieval:SilentCamelsStick",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:ItchyRiversDrum",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Wikipedia:WittyRiceLearnb-Generate:ItchyRiversDrumb",
- "markerEnd": "logo",
- "source": "Wikipedia:WittyRiceLearn",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:ItchyRiversDrum",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__Baidu:OliveAreasCallb-Generate:ItchyRiversDrumb",
- "markerEnd": "logo",
- "source": "Baidu:OliveAreasCall",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:ItchyRiversDrum",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- },
- {
- "id": "xy-edge__DuckDuckGo:SoftButtonsRefuseb-Generate:ItchyRiversDrumb",
- "markerEnd": "logo",
- "source": "DuckDuckGo:SoftButtonsRefuse",
- "sourceHandle": "b",
- "style": {
- "stroke": "rgb(202 197 245)",
- "strokeWidth": 2
- },
- "target": "Generate:ItchyRiversDrum",
- "targetHandle": "b",
- "type": "buttonEdge",
- "zIndex": 1001
- }
- ],
- "nodes": [
- {
- "data": {
- "label": "Begin",
- "name": "opening"
- },
- "dragging": false,
- "height": 44,
- "id": "begin",
- "measured": {
- "height": 44,
- "width": 100
- },
- "position": {
- "x": -1469.1118402678153,
- "y": -138.55389910599428
- },
- "positionAbsolute": {
- "x": -1379.627471412851,
- "y": -135.63593055637585
- },
- "selected": false,
- "sourcePosition": "left",
- "targetPosition": "right",
- "type": "beginNode"
- },
- {
- "data": {
- "form": {},
- "label": "Answer",
- "name": "interface"
- },
- "dragging": false,
- "height": 44,
- "id": "Answer:PoorMapsCover",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": -1172.8677760724227,
- "y": -134.7856818291531
- },
- "positionAbsolute": {
- "x": -1172.8677760724227,
- "y": -134.7856818291531
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "logicNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "language": "en",
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- },
- "label": "Wikipedia",
- "name": "Wikipedia"
- },
- "dragging": false,
- "height": 44,
- "id": "Wikipedia:WittyRiceLearn",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": -406.9217458441634,
- "y": -54.01023495053805
- },
- "positionAbsolute": {
- "x": -406.9217458441634,
- "y": -54.01023495053805
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- },
- "label": "Baidu",
- "name": "Baidu"
- },
- "dragging": false,
- "height": 44,
- "id": "Baidu:OliveAreasCall",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": -334.8102520664264,
- "y": -142.4206828864257
- },
- "positionAbsolute": {
- "x": -334.8102520664264,
- "y": -142.4206828864257
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "channel": "text",
- "query": [
- {
- "component_id": "KeywordExtract:BeigeTipsStand",
- "type": "reference"
- }
- ],
- "top_n": 2
- },
- "label": "DuckDuckGo",
- "name": "DuckDuckGo"
- },
- "dragging": false,
- "height": 44,
- "id": "DuckDuckGo:SoftButtonsRefuse",
- "measured": {
- "height": 44,
- "width": 200
- },
- "position": {
- "x": -241.42135935727495,
- "y": -227.69429585279033
- },
- "positionAbsolute": {
- "x": -241.42135935727495,
- "y": -227.69429585279033
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "ragNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "loop": 1,
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "message_history_window_size": 6,
- "parameter": "Precise",
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "RewriteQuestion",
- "name": "Refine Question"
- },
- "dragging": false,
- "height": 86,
- "id": "RewriteQuestion:OrangeBottlesSwim",
- "measured": {
- "height": 86,
- "width": 200
- },
- "position": {
- "x": -926.3250837910092,
- "y": -156.41315582042822
- },
- "positionAbsolute": {
- "x": -926.3250837910092,
- "y": -156.41315582042822
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "rewriteNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": true,
- "max_tokens": 256,
- "parameter": "Precise",
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_n": 2,
- "top_p": 0.3
- },
- "label": "KeywordExtract",
- "name": "Get keywords"
- },
- "dragging": false,
- "height": 86,
- "id": "KeywordExtract:BeigeTipsStand",
- "measured": {
- "height": 86,
- "width": 200
- },
- "position": {
- "x": -643.95039088561,
- "y": -160.37167955274685
- },
- "positionAbsolute": {
- "x": -643.95039088561,
- "y": -160.37167955274685
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "keywordNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "empty_response": "The answer you want was not found in the knowledge base!",
- "kb_ids": [],
- "keywords_similarity_weight": 0.3,
- "similarity_threshold": 0.2,
- "top_n": 8
- },
- "label": "Retrieval",
- "name": "Search KB"
- },
- "dragging": false,
- "height": 46,
- "id": "Retrieval:SilentCamelsStick",
- "measured": {
- "height": 46,
- "width": 200
- },
- "position": {
- "x": -641.3113750640641,
- "y": -4.669746081545384
- },
- "positionAbsolute": {
- "x": -641.3113750640641,
- "y": -4.669746081545384
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "retrievalNode",
- "width": 200
- },
- {
- "data": {
- "form": {
- "text": "The large model answers the user's query based on the content retrieved from different search engines and knowledge bases, returning an answer to the user's question."
- },
- "label": "Note",
- "name": "N: LLM"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 144,
- "id": "Note:CuteSchoolsWear",
- "measured": {
- "height": 144,
- "width": 443
- },
- "position": {
- "x": -628.5256394373041,
- "y": 412.60472782016245
- },
- "positionAbsolute": {
- "x": -628.5256394373041,
- "y": 412.60472782016245
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 144,
- "width": 443
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 443
- },
- {
- "data": {
- "form": {
- "text": "Complete questions by conversation history.\nUser: What's RAGFlow?\nAssistant: RAGFlow is xxx.\nUser: How to deloy it?\n\nRefine it: How to deploy RAGFlow?"
- },
- "label": "Note",
- "name": "N: Refine question"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 209,
- "id": "Note:CuteRavensBehave",
- "measured": {
- "height": 209,
- "width": 266
- },
- "position": {
- "x": -921.2271023677847,
- "y": -381.3182401779728
- },
- "positionAbsolute": {
- "x": -921.2271023677847,
- "y": -381.3182401779728
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 209,
- "width": 266
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 266
- },
- {
- "data": {
- "form": {
- "text": "Based on the user's question, searches the knowledge base and returns the retrieved content."
- },
- "label": "Note",
- "name": "N: Search KB"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 128,
- "id": "Note:RudeRulesLeave",
- "measured": {
- "height": 128,
- "width": 269
- },
- "position": {
- "x": -917.896611693436,
- "y": -3.570404025438563
- },
- "positionAbsolute": {
- "x": -917.896611693436,
- "y": -3.570404025438563
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 269
- },
- {
- "data": {
- "form": {
- "text": "Based on the keywords, searches on Wikipedia and returns the found content."
- },
- "label": "Note",
- "name": "N: Wikipedia"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 128,
- "id": "Note:DryActorsTry",
- "measured": {
- "height": 128,
- "width": 281
- },
- "position": {
- "x": 49.68127281474659,
- "y": -16.899164744846445
- },
- "positionAbsolute": {
- "x": 49.68127281474659,
- "y": -16.899164744846445
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 128,
- "width": 281
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 281
- },
- {
- "data": {
- "form": {
- "text": "Based on the keywords, searches on Baidu and returns the found content."
- },
- "label": "Note",
- "name": "N :Baidu"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 128,
- "id": "Note:HonestShirtsNail",
- "measured": {
- "height": 128,
- "width": 269
- },
- "position": {
- "x": 43.964372149616565,
- "y": -151.26282396084338
- },
- "positionAbsolute": {
- "x": 43.964372149616565,
- "y": -151.26282396084338
- },
- "selected": false,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "noteNode",
- "width": 269
- },
- {
- "data": {
- "form": {
- "text": "Based on the keywords, searches on DuckDuckGo and returns the found content."
- },
- "label": "Note",
- "name": "N: DuckduckGo"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 145,
- "id": "Note:OddBreadsFix",
- "measured": {
- "height": 145,
- "width": 201
- },
- "position": {
- "x": -237.54626926201882,
- "y": -381.56637252684175
- },
- "positionAbsolute": {
- "x": -237.54626926201882,
- "y": -381.56637252684175
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 145,
- "width": 201
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 201
- },
- {
- "data": {
- "form": {
- "text": "The large model generates keywords based on the user's question for better retrieval."
- },
- "label": "Note",
- "name": "N: Get keywords"
- },
- "dragHandle": ".note-drag-handle",
- "dragging": false,
- "height": 162,
- "id": "Note:GentleWorldsDesign",
- "measured": {
- "height": 162,
- "width": 201
- },
- "position": {
- "x": -646.3211655055846,
- "y": -334.10598887579624
- },
- "positionAbsolute": {
- "x": -646.3211655055846,
- "y": -334.10598887579624
- },
- "resizing": false,
- "selected": false,
- "sourcePosition": "right",
- "style": {
- "height": 162,
- "width": 201
- },
- "targetPosition": "left",
- "type": "noteNode",
- "width": 201
- },
- {
- "data": {
- "form": {
- "cite": true,
- "frequencyPenaltyEnabled": true,
- "frequency_penalty": 0.7,
- "llm_id": "deepseek-chat@DeepSeek",
- "maxTokensEnabled": false,
- "max_tokens": 256,
- "message_history_window_size": 12,
- "parameter": "Precise",
- "parameters": [],
- "presencePenaltyEnabled": true,
- "presence_penalty": 0.4,
- "prompt": "Role: You are an intelligent assistant. \nTask: Chat with user. Answer the question based on the provided content from: Knowledge Base, Wikipedia, Duckduckgo, Baidu.\nRequirements:\n - Answer should be in markdown format.\n - Answer should include all sources(Knowledge Base, Wikipedia, Duckduckgo, Baidu) as long as they are relevant, and label the sources of the cited content separately.\n - Attach URL links to the content which is quoted from Wikipedia, DuckDuckGo or Baidu.\n - Do not make thing up when there's no relevant information to user's question. \n\n## Knowledge base content\n{Retrieval:SilentCamelsStick}\n\n\n## Wikipedia content\n{Wikipedia:WittyRiceLearn}\n\n\n## Duckduckgo content\n{DuckDuckGo:SoftButtonsRefuse}\n\n\n## Baidu content\n{Baidu:OliveAreasCall}\n\n",
- "temperature": 0.1,
- "temperatureEnabled": true,
- "topPEnabled": true,
- "top_p": 0.3
- },
- "label": "Generate",
- "name": "LLM"
- },
- "dragging": false,
- "id": "Generate:ItchyRiversDrum",
- "measured": {
- "height": 108,
- "width": 200
- },
- "position": {
- "x": -636.2454246475879,
- "y": 282.00479262604443
- },
- "selected": true,
- "sourcePosition": "right",
- "targetPosition": "left",
- "type": "generateNode"
- }
- ]
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": []
- },
- "avatar": "data:image/png;base64,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"
-}
diff --git a/agent/test/client.py b/agent/test/client.py
index 1ab4db386..09b685e43 100644
--- a/agent/test/client.py
+++ b/agent/test/client.py
@@ -15,9 +15,8 @@
#
import argparse
import os
-from functools import partial
from agent.canvas import Canvas
-from agent.settings import DEBUG
+from api import settings
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@@ -31,19 +30,17 @@ if __name__ == '__main__':
parser.add_argument('-m', '--stream', default=False, help="Stream output", action='store_true', required=False)
args = parser.parse_args()
+ settings.init_settings()
canvas = Canvas(open(args.dsl, "r").read(), args.tenant_id)
+ if canvas.get_prologue():
+ print(f"==================== Bot =====================\n> {canvas.get_prologue()}", end='')
+ query = ""
while True:
- ans = canvas.run(stream=args.stream)
+ canvas.reset(True)
+ query = input("\n==================== User =====================\n> ")
+ ans = canvas.run(query=query)
print("==================== Bot =====================\n> ", end='')
- if args.stream and isinstance(ans, partial):
- cont = ""
- for an in ans():
- print(an["content"][len(cont):], end='', flush=True)
- cont = an["content"]
- else:
- print(ans["content"])
+ for ans in canvas.run(query=query):
+ print(ans, end='\n', flush=True)
- if DEBUG:
- print(canvas.path)
- question = input("\n==================== User =====================\n> ")
- canvas.add_user_input(question)
+ print(canvas.path)
diff --git a/agent/test/dsl_examples/baidu_generate_and_switch.json b/agent/test/dsl_examples/baidu_generate_and_switch.json
deleted file mode 100644
index 90069cfaf..000000000
--- a/agent/test/dsl_examples/baidu_generate_and_switch.json
+++ /dev/null
@@ -1,129 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["baidu:0"],
- "upstream": ["begin", "message:0","message:1"]
- },
- "baidu:0": {
- "obj": {
- "component_name": "Baidu",
- "params": {}
- },
- "downstream": ["generate:0"],
- "upstream": ["answer:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the user's question based on what Baidu searched. First, please output the user's question and the content searched by Baidu, and then answer yes, no, or i don't know.Here is the user's question:{user_input}The above is the user's question.Here is what Baidu searched for:{baidu}The above is the content searched by Baidu.",
- "temperature": 0.2
- },
- "parameters": [
- {
- "component_id": "answer:0",
- "id": "69415446-49bf-4d4b-8ec9-ac86066f7709",
- "key": "user_input"
- },
- {
- "component_id": "baidu:0",
- "id": "83363c2a-00a8-402f-a45c-ddc4097d7d8b",
- "key": "baidu"
- }
- ]
- },
- "downstream": ["switch:0"],
- "upstream": ["baidu:0"]
- },
- "switch:0": {
- "obj": {
- "component_name": "Switch",
- "params": {
- "conditions": [
- {
- "logical_operator" : "or",
- "items" : [
- {"cpn_id": "generate:0", "operator": "contains", "value": "yes"},
- {"cpn_id": "generate:0", "operator": "contains", "value": "yeah"}
- ],
- "to": "message:0"
- },
- {
- "logical_operator" : "and",
- "items" : [
- {"cpn_id": "generate:0", "operator": "contains", "value": "no"},
- {"cpn_id": "generate:0", "operator": "not contains", "value": "yes"},
- {"cpn_id": "generate:0", "operator": "not contains", "value": "know"}
- ],
- "to": "message:1"
- },
- {
- "logical_operator" : "",
- "items" : [
- {"cpn_id": "generate:0", "operator": "contains", "value": "know"}
- ],
- "to": "message:2"
- }
- ],
- "end_cpn_id": "answer:0"
-
- }
- },
- "downstream": ["message:0","message:1"],
- "upstream": ["generate:0"]
- },
- "message:0": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": ["YES YES YES YES YES YES YES YES YES YES YES YES"]
- }
- },
-
- "upstream": ["switch:0"],
- "downstream": ["answer:0"]
- },
- "message:1": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": ["NO NO NO NO NO NO NO NO NO NO NO NO NO NO"]
- }
- },
-
- "upstream": ["switch:0"],
- "downstream": ["answer:0"]
- },
- "message:2": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": ["I DON'T KNOW---------------------------"]
- }
- },
-
- "upstream": ["switch:0"],
- "downstream": ["answer:0"]
- }
- },
- "history": [],
- "messages": [],
- "reference": {},
- "path": [],
- "answer": []
-}
diff --git a/agent/test/dsl_examples/categorize.json b/agent/test/dsl_examples/categorize_and_agent_with_tavily.json
similarity index 54%
rename from agent/test/dsl_examples/categorize.json
rename to agent/test/dsl_examples/categorize_and_agent_with_tavily.json
index 600c9bc3f..7d9567446 100644
--- a/agent/test/dsl_examples/categorize.json
+++ b/agent/test/dsl_examples/categorize_and_agent_with_tavily.json
@@ -7,16 +7,8 @@
"prologue": "Hi there!"
}
},
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
"downstream": ["categorize:0"],
- "upstream": ["begin"]
+ "upstream": []
},
"categorize:0": {
"obj": {
@@ -26,48 +18,68 @@
"category_description": {
"product_related": {
"description": "The question is about the product usage, appearance and how it works.",
- "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
- "to": "message:0"
+ "to": ["agent:0"]
},
"others": {
"description": "The question is not about the product usage, appearance and how it works.",
- "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
- "to": "message:1"
+ "to": ["message:0"]
}
}
}
},
- "downstream": ["message:0","message:1"],
- "upstream": ["answer:0"]
+ "downstream": [],
+ "upstream": ["begin"]
},
"message:0": {
- "obj": {
+ "obj":{
"component_name": "Message",
"params": {
- "messages": [
- "Message 0!!!!!!!"
+ "content": [
+ "Sorry, I don't know. I'm an AI bot."
]
}
},
- "downstream": ["answer:0"],
+ "downstream": [],
+ "upstream": ["categorize:0"]
+ },
+ "agent:0": {
+ "obj": {
+ "component_name": "Agent",
+ "params": {
+ "llm_id": "deepseek-chat",
+ "sys_prompt": "You are a smart researcher. You could generate proper queries to search. According to the search results, you could deside next query if the result is not enough.",
+ "temperature": 0.2,
+ "llm_enabled_tools": [
+ {
+ "component_name": "TavilySearch",
+ "params": {
+ "api_key": "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"
+ }
+ }
+ ]
+ }
+ },
+ "downstream": ["message:1"],
"upstream": ["categorize:0"]
},
"message:1": {
"obj": {
"component_name": "Message",
"params": {
- "messages": [
- "Message 1!!!!!!!"
- ]
+ "content": ["{agent:0@content}"]
}
},
- "downstream": ["answer:0"],
- "upstream": ["categorize:0"]
+ "downstream": [],
+ "upstream": ["agent:0"]
}
},
"history": [],
- "messages": [],
"path": [],
- "reference": [],
- "answer": []
-}
+ "retrival": {"chunks": [], "doc_aggs": []},
+ "globals": {
+ "sys.query": "",
+ "sys.user_id": "",
+ "sys.conversation_turns": 0,
+ "sys.files": []
+ }
+}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/concentrator_message.json b/agent/test/dsl_examples/concentrator_message.json
deleted file mode 100644
index ee875ae02..000000000
--- a/agent/test/dsl_examples/concentrator_message.json
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["categorize:0"],
- "upstream": ["begin"]
- },
- "categorize:0": {
- "obj": {
- "component_name": "Categorize",
- "params": {
- "llm_id": "deepseek-chat",
- "category_description": {
- "product_related": {
- "description": "The question is about the product usage, appearance and how it works.",
- "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
- "to": "concentrator:0"
- },
- "others": {
- "description": "The question is not about the product usage, appearance and how it works.",
- "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
- "to": "concentrator:1"
- }
- }
- }
- },
- "downstream": ["concentrator:0","concentrator:1"],
- "upstream": ["answer:0"]
- },
- "concentrator:0": {
- "obj": {
- "component_name": "Concentrator",
- "params": {}
- },
- "downstream": ["message:0"],
- "upstream": ["categorize:0"]
- },
- "concentrator:1": {
- "obj": {
- "component_name": "Concentrator",
- "params": {}
- },
- "downstream": ["message:1_0","message:1_1","message:1_2"],
- "upstream": ["categorize:0"]
- },
- "message:0": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": [
- "Message 0_0!!!!!!!"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["concentrator:0"]
- },
- "message:1_0": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": [
- "Message 1_0!!!!!!!"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["concentrator:1"]
- },
- "message:1_1": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": [
- "Message 1_1!!!!!!!"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["concentrator:1"]
- },
- "message:1_2": {
- "obj": {
- "component_name": "Message",
- "params": {
- "messages": [
- "Message 1_2!!!!!!!"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["concentrator:1"]
- }
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": [],
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/customer_service.json b/agent/test/dsl_examples/customer_service.json
deleted file mode 100644
index 8421e3a26..000000000
--- a/agent/test/dsl_examples/customer_service.json
+++ /dev/null
@@ -1,157 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi! How can I help you?"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["categorize:0"],
- "upstream": ["begin", "generate:0", "generate:casual", "generate:answer", "generate:complain", "generate:ask_contact", "message:get_contact"]
- },
- "categorize:0": {
- "obj": {
- "component_name": "Categorize",
- "params": {
- "llm_id": "deepseek-chat",
- "category_description": {
- "product_related": {
- "description": "The question is about the product usage, appearance and how it works.",
- "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?\nException: Can't connect to ES cluster\nHow to build the RAGFlow image from scratch",
- "to": "retrieval:0"
- },
- "casual": {
- "description": "The question is not about the product usage, appearance and how it works. Just casual chat.",
- "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
- "to": "generate:casual"
- },
- "complain": {
- "description": "Complain even curse about the product or service you provide. But the comment is not specific enough.",
- "examples": "How bad is it.\nIt's really sucks.\nDamn, for God's sake, can it be more steady?\nShit, I just can't use this shit.\nI can't stand it anymore.",
- "to": "generate:complain"
- },
- "answer": {
- "description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.",
- "examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384",
- "to": "message:get_contact"
- }
- },
- "message_history_window_size": 8
- }
- },
- "downstream": ["retrieval:0", "generate:casual", "generate:complain", "message:get_contact"],
- "upstream": ["answer:0"]
- },
- "generate:casual": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are a customer support. But the customer wants to have a casual chat with you instead of consulting about the product. Be nice, funny, enthusiasm and concern.",
- "temperature": 0.9,
- "message_history_window_size": 12,
- "cite": false
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["categorize:0"]
- },
- "generate:complain": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are a customer support. the Customers complain even curse about the products but not specific enough. You need to ask him/her what's the specific problem with the product. Be nice, patient and concern to soothe your customers’ emotions at first place.",
- "temperature": 0.9,
- "message_history_window_size": 12,
- "cite": false
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["categorize:0"]
- },
- "retrieval:0": {
- "obj": {
- "component_name": "Retrieval",
- "params": {
- "similarity_threshold": 0.2,
- "keywords_similarity_weight": 0.3,
- "top_n": 6,
- "top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["869a236818b811ef91dffa163e197198"]
- }
- },
- "downstream": ["relevant:0"],
- "upstream": ["categorize:0"]
- },
- "relevant:0": {
- "obj": {
- "component_name": "Relevant",
- "params": {
- "llm_id": "deepseek-chat",
- "temperature": 0.02,
- "yes": "generate:answer",
- "no": "generate:ask_contact"
- }
- },
- "downstream": ["generate:answer", "generate:ask_contact"],
- "upstream": ["retrieval:0"]
- },
- "generate:answer": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
- "temperature": 0.02
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- },
- "generate:ask_contact": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are a customer support. But you can't answer to customers' question. You need to request their contact like E-mail, phone number, Wechat number, LINE number, twitter, discord, etc,. Product experts will contact them later. Please do not ask the same question twice.",
- "temperature": 0.9,
- "message_history_window_size": 12,
- "cite": false
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- },
- "message:get_contact": {
- "obj":{
- "component_name": "Message",
- "params": {
- "messages": [
- "Okay, I've already write this down. What else I can do for you?",
- "Get it. What else I can do for you?",
- "Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?",
- "Thanks! So, anything else I can do for you?"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["categorize:0"]
- }
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": [],
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/intergreper.json b/agent/test/dsl_examples/intergreper.json
deleted file mode 100644
index e528b2756..000000000
--- a/agent/test/dsl_examples/intergreper.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["generate:0"],
- "upstream": ["begin", "generate:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n",
- "temperature": 0.5
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["answer:0"]
- }
- },
- "history": [],
- "messages": [],
- "reference": {},
- "path": [],
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/interpreter.json b/agent/test/dsl_examples/interpreter.json
deleted file mode 100644
index e528b2756..000000000
--- a/agent/test/dsl_examples/interpreter.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["generate:0"],
- "upstream": ["begin", "generate:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n",
- "temperature": 0.5
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["answer:0"]
- }
- },
- "history": [],
- "messages": [],
- "reference": {},
- "path": [],
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/iteration.json b/agent/test/dsl_examples/iteration.json
new file mode 100644
index 000000000..dd4448423
--- /dev/null
+++ b/agent/test/dsl_examples/iteration.json
@@ -0,0 +1,92 @@
+{
+ "components": {
+ "begin": {
+ "obj":{
+ "component_name": "Begin",
+ "params": {
+ "prologue": "Hi there!"
+ }
+ },
+ "downstream": ["generate:0"],
+ "upstream": []
+ },
+ "generate:0": {
+ "obj": {
+ "component_name": "Agent",
+ "params": {
+ "llm_id": "deepseek-chat",
+ "sys_prompt": "You are an helpful research assistant. \nPlease decompose user's topic: '{sys.query}' into several meaningful sub-topics. \nThe output format MUST be an string array like: [\"sub-topic1\", \"sub-topic2\", ...]. Redundant information is forbidden.",
+ "temperature": 0.2,
+ "cite":false,
+ "output_structure": ["sub-topic1", "sub-topic2", "sub-topic3"]
+ }
+ },
+ "downstream": ["iteration:0"],
+ "upstream": ["begin"]
+ },
+ "iteration:0": {
+ "obj": {
+ "component_name": "Iteration",
+ "params": {
+ "items_ref": "generate:0@structured_content"
+ }
+ },
+ "downstream": ["message:0"],
+ "upstream": ["generate:0"]
+ },
+ "iterationitem:0": {
+ "obj": {
+ "component_name": "IterationItem",
+ "params": {}
+ },
+ "parent_id": "iteration:0",
+ "downstream": ["tavily:0"],
+ "upstream": []
+ },
+ "tavily:0": {
+ "obj": {
+ "component_name": "TavilySearch",
+ "params": {
+ "api_key": "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",
+ "query": "iterationitem:0@result"
+ }
+ },
+ "parent_id": "iteration:0",
+ "downstream": ["generate:1"],
+ "upstream": ["iterationitem:0"]
+ },
+ "generate:1": {
+ "obj": {
+ "component_name": "Agent",
+ "params": {
+ "llm_id": "deepseek-chat",
+ "sys_prompt": "Your goal is to provide answers based on information from the internet. \nYou must use the provided search results to find relevant online information. \nYou should never use your own knowledge to answer questions.\nPlease include relevant url sources in the end of your answers.\n\n \"{tavily:0@formalized_content}\" \nUsing the above information, answer the following question or topic: \"{iterationitem:0@result} \"\nin a detailed report — The report should focus on the answer to the question, should be well structured, informative, in depth, with facts and numbers if available, a minimum of 200 words and with markdown syntax and apa format. Write all source urls at the end of the report in apa format. You should write your report only based on the given information and nothing else.",
+ "temperature": 0.9,
+ "cite":false
+ }
+ },
+ "parent_id": "iteration:0",
+ "downstream": ["iterationitem:0"],
+ "upstream": ["tavily:0"]
+ },
+ "message:0": {
+ "obj": {
+ "component_name": "Message",
+ "params": {
+ "content": ["{iteration:0@generate:1}"]
+ }
+ },
+ "downstream": [],
+ "upstream": ["iteration:0"]
+ }
+ },
+ "history": [],
+ "path": [],
+ "retrival": {"chunks": [], "doc_aggs": []},
+ "globals": {
+ "sys.query": "",
+ "sys.user_id": "",
+ "sys.conversation_turns": 0,
+ "sys.files": []
+ }
+}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/keyword_wikipedia_and_generate.json b/agent/test/dsl_examples/keyword_wikipedia_and_generate.json
deleted file mode 100644
index fa1d62194..000000000
--- a/agent/test/dsl_examples/keyword_wikipedia_and_generate.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["keyword:0"],
- "upstream": ["begin"]
- },
- "keyword:0": {
- "obj": {
- "component_name": "KeywordExtract",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "- Role: You're a question analyzer.\n - Requirements:\n - Summarize user's question, and give top %s important keyword/phrase.\n - Use comma as a delimiter to separate keywords/phrases.\n - Answer format: (in language of user's question)\n - keyword: ",
- "temperature": 0.2,
- "top_n": 1
- }
- },
- "downstream": ["wikipedia:0"],
- "upstream": ["answer:0"]
- },
- "wikipedia:0": {
- "obj":{
- "component_name": "Wikipedia",
- "params": {
- "top_n": 10
- }
- },
- "downstream": ["generate:0"],
- "upstream": ["keyword:0"]
- },
- "generate:1": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content from Wikipedia. When the answer from Wikipedia is incomplete, you need to output the URL link of the corresponding content as well. When all the content searched from Wikipedia is irrelevant to the question, your answer must include the sentence, \"The answer you are looking for is not found in the Wikipedia!\". Answers need to consider chat history.\n The content of Wikipedia is as follows:\n {input}\n The above is the content of Wikipedia.",
- "temperature": 0.2
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["wikipedia:0"]
- }
- },
- "history": [],
- "path": [],
- "messages": [],
- "reference": {},
- "answer": []
-}
diff --git a/agent/test/dsl_examples/retrieval_and_generate.json b/agent/test/dsl_examples/retrieval_and_generate.json
index fbbf076aa..9f9f9bac4 100644
--- a/agent/test/dsl_examples/retrieval_and_generate.json
+++ b/agent/test/dsl_examples/retrieval_and_generate.json
@@ -7,16 +7,8 @@
"prologue": "Hi there!"
}
},
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
"downstream": ["retrieval:0"],
- "upstream": ["begin", "generate:0"]
+ "upstream": []
},
"retrieval:0": {
"obj": {
@@ -26,29 +18,44 @@
"keywords_similarity_weight": 0.3,
"top_n": 6,
"top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["869a236818b811ef91dffa163e197198"]
+ "rerank_id": "",
+ "empty_response": "Nothing found in dataset",
+ "kb_ids": ["1a3d1d7afb0611ef9866047c16ec874f"]
}
},
"downstream": ["generate:0"],
- "upstream": ["answer:0"]
+ "upstream": ["begin"]
},
"generate:0": {
"obj": {
- "component_name": "Generate",
+ "component_name": "LLM",
"params": {
"llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.",
+ "sys_prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {retrieval:0@formalized_content}\n The above is the knowledge base.",
"temperature": 0.2
}
},
- "downstream": ["answer:0"],
+ "downstream": ["message:0"],
"upstream": ["retrieval:0"]
+ },
+ "message:0": {
+ "obj": {
+ "component_name": "Message",
+ "params": {
+ "content": ["{generate:0@content}"]
+ }
+ },
+ "downstream": [],
+ "upstream": ["generate:0"]
}
},
"history": [],
- "messages": [],
- "reference": {},
"path": [],
- "answer": []
+ "retrival": {"chunks": [], "doc_aggs": []},
+ "globals": {
+ "sys.query": "",
+ "sys.user_id": "",
+ "sys.conversation_turns": 0,
+ "sys.files": []
+ }
}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/retrieval_categorize_and_generate.json b/agent/test/dsl_examples/retrieval_categorize_and_generate.json
index 4276b3330..c506b9a6b 100644
--- a/agent/test/dsl_examples/retrieval_categorize_and_generate.json
+++ b/agent/test/dsl_examples/retrieval_categorize_and_generate.json
@@ -7,16 +7,8 @@
"prologue": "Hi there!"
}
},
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
"downstream": ["categorize:0"],
- "upstream": ["begin", "generate:0", "switch:0"]
+ "upstream": []
},
"categorize:0": {
"obj": {
@@ -26,30 +18,30 @@
"category_description": {
"product_related": {
"description": "The question is about the product usage, appearance and how it works.",
- "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
- "to": "retrieval:0"
+ "examples": [],
+ "to": ["retrieval:0"]
},
"others": {
"description": "The question is not about the product usage, appearance and how it works.",
- "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
- "to": "message:0"
+ "examples": [],
+ "to": ["message:0"]
}
}
}
},
- "downstream": ["retrieval:0", "message:0"],
- "upstream": ["answer:0"]
+ "downstream": [],
+ "upstream": ["begin"]
},
"message:0": {
"obj":{
"component_name": "Message",
"params": {
- "messages": [
+ "content": [
"Sorry, I don't know. I'm an AI bot."
]
}
},
- "downstream": ["answer:0"],
+ "downstream": [],
"upstream": ["categorize:0"]
},
"retrieval:0": {
@@ -60,29 +52,44 @@
"keywords_similarity_weight": 0.3,
"top_n": 6,
"top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["869a236818b811ef91dffa163e197198"]
+ "rerank_id": "",
+ "empty_response": "Nothing found in dataset",
+ "kb_ids": ["1a3d1d7afb0611ef9866047c16ec874f"]
}
},
"downstream": ["generate:0"],
- "upstream": ["switch:0"]
+ "upstream": ["categorize:0"]
},
"generate:0": {
"obj": {
- "component_name": "Generate",
+ "component_name": "Agent",
"params": {
"llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.",
+ "sys_prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {retrieval:0@formalized_content}\n The above is the knowledge base.",
"temperature": 0.2
}
},
- "downstream": ["answer:0"],
+ "downstream": ["message:1"],
"upstream": ["retrieval:0"]
+ },
+ "message:1": {
+ "obj": {
+ "component_name": "Message",
+ "params": {
+ "content": ["{generate:0@content}"]
+ }
+ },
+ "downstream": [],
+ "upstream": ["generate:0"]
}
},
"history": [],
- "messages": [],
- "reference": {},
"path": [],
- "answer": []
+ "retrival": {"chunks": [], "doc_aggs": []},
+ "globals": {
+ "sys.query": "",
+ "sys.user_id": "",
+ "sys.conversation_turns": 0,
+ "sys.files": []
+ }
}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/retrieval_relevant_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_and_generate.json
deleted file mode 100644
index 8eeb3237d..000000000
--- a/agent/test/dsl_examples/retrieval_relevant_and_generate.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["retrieval:0"],
- "upstream": ["begin", "generate:0", "switch:0"]
- },
- "retrieval:0": {
- "obj": {
- "component_name": "Retrieval",
- "params": {
- "similarity_threshold": 0.2,
- "keywords_similarity_weight": 0.3,
- "top_n": 6,
- "top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["869a236818b811ef91dffa163e197198"],
- "empty_response": "Sorry, knowledge base has noting related information."
- }
- },
- "downstream": ["relevant:0"],
- "upstream": ["answer:0"]
- },
- "relevant:0": {
- "obj": {
- "component_name": "Relevant",
- "params": {
- "llm_id": "deepseek-chat",
- "temperature": 0.02,
- "yes": "generate:0",
- "no": "message:0"
- }
- },
- "downstream": ["message:0", "generate:0"],
- "upstream": ["retrieval:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
- "temperature": 0.2
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- },
- "message:0": {
- "obj":{
- "component_name": "Message",
- "params": {
- "messages": [
- "Sorry, I don't know. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?",
- "I'm an AI bot and not quite sure about this question. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?",
- "Can't find answer in my knowledge base. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?"
- ]
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- }
- },
- "history": [],
- "path": [],
- "messages": [],
- "reference": {},
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json
deleted file mode 100644
index a34b58a36..000000000
--- a/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json
+++ /dev/null
@@ -1,103 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["retrieval:0"],
- "upstream": ["begin"]
- },
- "retrieval:0": {
- "obj": {
- "component_name": "Retrieval",
- "params": {
- "similarity_threshold": 0.2,
- "keywords_similarity_weight": 0.3,
- "top_n": 6,
- "top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["21ca4e6a2c8911ef8b1e0242ac120006"],
- "empty_response": "Sorry, knowledge base has noting related information."
- }
- },
- "downstream": ["relevant:0"],
- "upstream": ["answer:0"]
- },
- "relevant:0": {
- "obj": {
- "component_name": "Relevant",
- "params": {
- "llm_id": "deepseek-chat",
- "temperature": 0.02,
- "yes": "generate:0",
- "no": "keyword:0"
- }
- },
- "downstream": ["keyword:0", "generate:0"],
- "upstream": ["retrieval:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
- "temperature": 0.2
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- },
- "keyword:0": {
- "obj": {
- "component_name": "KeywordExtract",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "- Role: You're a question analyzer.\n - Requirements:\n - Summarize user's question, and give top %s important keyword/phrase.\n - Use comma as a delimiter to separate keywords/phrases.\n - Answer format: (in language of user's question)\n - keyword: ",
- "temperature": 0.2,
- "top_n": 1
- }
- },
- "downstream": ["baidu:0"],
- "upstream": ["relevant:0"]
- },
- "baidu:0": {
- "obj":{
- "component_name": "Baidu",
- "params": {
- "top_n": 10
- }
- },
- "downstream": ["generate:1"],
- "upstream": ["keyword:0"]
- },
- "generate:1": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content searched from Baidu. When the answer from a Baidu search is incomplete, you need to output the URL link of the corresponding content as well. When all the content searched from Baidu is irrelevant to the question, your answer must include the sentence, \"The answer you are looking for is not found in the Baidu search!\". Answers need to consider chat history.\n The content of Baidu search is as follows:\n {input}\n The above is the content of Baidu search.",
- "temperature": 0.2
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["baidu:0"]
- }
- },
- "history": [],
- "path": [],
- "messages": [],
- "reference": {},
- "answer": []
-}
diff --git a/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json
deleted file mode 100644
index fb290a2ec..000000000
--- a/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "components": {
- "begin": {
- "obj":{
- "component_name": "Begin",
- "params": {
- "prologue": "Hi there!"
- }
- },
- "downstream": ["answer:0"],
- "upstream": []
- },
- "answer:0": {
- "obj": {
- "component_name": "Answer",
- "params": {}
- },
- "downstream": ["retrieval:0"],
- "upstream": ["begin", "generate:0", "switch:0"]
- },
- "retrieval:0": {
- "obj": {
- "component_name": "Retrieval",
- "params": {
- "similarity_threshold": 0.2,
- "keywords_similarity_weight": 0.3,
- "top_n": 6,
- "top_k": 1024,
- "rerank_id": "BAAI/bge-reranker-v2-m3",
- "kb_ids": ["869a236818b811ef91dffa163e197198"],
- "empty_response": "Sorry, knowledge base has noting related information."
- }
- },
- "downstream": ["relevant:0"],
- "upstream": ["answer:0", "rewrite:0"]
- },
- "relevant:0": {
- "obj": {
- "component_name": "Relevant",
- "params": {
- "llm_id": "deepseek-chat",
- "temperature": 0.02,
- "yes": "generate:0",
- "no": "rewrite:0"
- }
- },
- "downstream": ["generate:0", "rewrite:0"],
- "upstream": ["retrieval:0"]
- },
- "generate:0": {
- "obj": {
- "component_name": "Generate",
- "params": {
- "llm_id": "deepseek-chat",
- "prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
- "temperature": 0.02
- }
- },
- "downstream": ["answer:0"],
- "upstream": ["relevant:0"]
- },
- "rewrite:0": {
- "obj":{
- "component_name": "RewriteQuestion",
- "params": {
- "llm_id": "deepseek-chat",
- "temperature": 0.8
- }
- },
- "downstream": ["retrieval:0"],
- "upstream": ["relevant:0"]
- }
- },
- "history": [],
- "messages": [],
- "path": [],
- "reference": [],
- "answer": []
-}
\ No newline at end of file
diff --git a/agent/test/dsl_examples/tavily_and_generate.json b/agent/test/dsl_examples/tavily_and_generate.json
new file mode 100644
index 000000000..f2f79b4b7
--- /dev/null
+++ b/agent/test/dsl_examples/tavily_and_generate.json
@@ -0,0 +1,55 @@
+{
+ "components": {
+ "begin": {
+ "obj":{
+ "component_name": "Begin",
+ "params": {
+ "prologue": "Hi there!"
+ }
+ },
+ "downstream": ["tavily:0"],
+ "upstream": []
+ },
+ "tavily:0": {
+ "obj": {
+ "component_name": "TavilySearch",
+ "params": {
+ "api_key": "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"
+ }
+ },
+ "downstream": ["generate:0"],
+ "upstream": ["begin"]
+ },
+ "generate:0": {
+ "obj": {
+ "component_name": "LLM",
+ "params": {
+ "llm_id": "deepseek-chat",
+ "sys_prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {tavily:0@formalized_content}\n The above is the knowledge base.",
+ "temperature": 0.2
+ }
+ },
+ "downstream": ["message:0"],
+ "upstream": ["tavily:0"]
+ },
+ "message:0": {
+ "obj": {
+ "component_name": "Message",
+ "params": {
+ "content": ["{generate:0@content}"]
+ }
+ },
+ "downstream": [],
+ "upstream": ["generate:0"]
+ }
+ },
+ "history": [],
+ "path": [],
+ "retrival": {"chunks": [], "doc_aggs": []},
+ "globals": {
+ "sys.query": "",
+ "sys.user_id": "",
+ "sys.conversation_turns": 0,
+ "sys.files": []
+ }
+}
\ No newline at end of file
diff --git a/agent/tools/__init__.py b/agent/tools/__init__.py
new file mode 100644
index 000000000..dcce4f448
--- /dev/null
+++ b/agent/tools/__init__.py
@@ -0,0 +1,33 @@
+import os
+import importlib
+import inspect
+from types import ModuleType
+from typing import Dict, Type
+
+_package_path = os.path.dirname(__file__)
+__all_classes: Dict[str, Type] = {}
+
+def _import_submodules() -> None:
+ for filename in os.listdir(_package_path): # noqa: F821
+ if filename.startswith("__") or not filename.endswith(".py") or filename.startswith("base"):
+ continue
+ module_name = filename[:-3]
+
+ try:
+ module = importlib.import_module(f".{module_name}", package=__name__)
+ _extract_classes_from_module(module) # noqa: F821
+ except ImportError as e:
+ print(f"Warning: Failed to import module {module_name}: {str(e)}")
+
+def _extract_classes_from_module(module: ModuleType) -> None:
+ for name, obj in inspect.getmembers(module):
+ if (inspect.isclass(obj) and
+ obj.__module__ == module.__name__ and not name.startswith("_")):
+ __all_classes[name] = obj
+ globals()[name] = obj
+
+_import_submodules()
+
+__all__ = list(__all_classes.keys()) + ["__all_classes"]
+
+del _package_path, _import_submodules, _extract_classes_from_module
\ No newline at end of file
diff --git a/agent/component/akshare.py b/agent/tools/akshare.py
similarity index 100%
rename from agent/component/akshare.py
rename to agent/tools/akshare.py
diff --git a/agent/tools/arxiv.py b/agent/tools/arxiv.py
new file mode 100644
index 000000000..08a3d1c66
--- /dev/null
+++ b/agent/tools/arxiv.py
@@ -0,0 +1,96 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+import arxiv
+from agent.tools.base import ToolParamBase, ToolMeta, ToolBase
+from api.utils.api_utils import timeout
+
+
+class ArXivParam(ToolParamBase):
+ """
+ Define the ArXiv component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "arxiv_search",
+ "description": """arXiv is a free distribution service and an open-access archive for nearly 2.4 million scholarly articles in the fields of physics, mathematics, computer science, quantitative biology, quantitative finance, statistics, electrical engineering and systems science, and economics. Materials on this site are not peer-reviewed by arXiv.""",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keywords to execute with arXiv. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 12
+ self.sort_by = 'submittedDate'
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+ self.check_valid_value(self.sort_by, "ArXiv Search Sort_by",
+ ['submittedDate', 'lastUpdatedDate', 'relevance'])
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+
+class ArXiv(ToolBase, ABC):
+ component_name = "ArXiv"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ sort_choices = {"relevance": arxiv.SortCriterion.Relevance,
+ "lastUpdatedDate": arxiv.SortCriterion.LastUpdatedDate,
+ 'submittedDate': arxiv.SortCriterion.SubmittedDate}
+ arxiv_client = arxiv.Client()
+ search = arxiv.Search(
+ query=kwargs["query"],
+ max_results=self._param.top_n,
+ sort_by=sort_choices[self._param.sort_by]
+ )
+ self._retrieve_chunks(list(arxiv_client.results(search)),
+ get_title=lambda r: r.title,
+ get_url=lambda r: r.pdf_url,
+ get_content=lambda r: r.summary)
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"ArXiv error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"ArXiv error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/tools/base.py b/agent/tools/base.py
new file mode 100644
index 000000000..5db9cf573
--- /dev/null
+++ b/agent/tools/base.py
@@ -0,0 +1,167 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import re
+import time
+from copy import deepcopy
+from functools import partial
+from typing import TypedDict, List, Any
+from agent.component.base import ComponentParamBase, ComponentBase
+from api.utils import hash_str2int
+from rag.llm.chat_model import ToolCallSession
+from rag.prompts.prompts import kb_prompt
+from rag.utils.mcp_tool_call_conn import MCPToolCallSession
+
+
+class ToolParameter(TypedDict):
+ type: str
+ description: str
+ displayDescription: str
+ enum: List[str]
+ required: bool
+
+
+class ToolMeta(TypedDict):
+ name: str
+ displayName: str
+ description: str
+ displayDescription: str
+ parameters: dict[str, ToolParameter]
+
+
+class LLMToolPluginCallSession(ToolCallSession):
+ def __init__(self, tools_map: dict[str, object], callback: partial):
+ self.tools_map = tools_map
+ self.callback = callback
+
+ def tool_call(self, name: str, arguments: dict[str, Any]) -> Any:
+ assert name in self.tools_map, f"LLM tool {name} does not exist"
+ self.callback(name, arguments, " running ...")
+ if isinstance(self.tools_map[name], MCPToolCallSession):
+ resp = self.tools_map[name].tool_call(name, arguments, 60)
+ else:
+ resp = self.tools_map[name].invoke(**arguments)
+ return resp
+
+ def get_tool_obj(self, name):
+ return self.tools_map[name]
+
+
+class ToolParamBase(ComponentParamBase):
+ def __init__(self):
+ #self.meta:ToolMeta = None
+ super().__init__()
+ self._init_inputs()
+ self._init_attr_by_meta()
+
+ def _init_inputs(self):
+ self.inputs = {}
+ for k,p in self.meta["parameters"].items():
+ self.inputs[k] = deepcopy(p)
+
+ def _init_attr_by_meta(self):
+ for k,p in self.meta["parameters"].items():
+ if not hasattr(self, k):
+ setattr(self, k, p.get("default"))
+
+ def get_meta(self):
+ params = {}
+ for k, p in self.meta["parameters"].items():
+ params[k] = {
+ "type": p["type"],
+ "description": p["description"]
+ }
+ if "enum" in p:
+ params[k]["enum"] = p["enum"]
+
+ desc = self.meta["description"]
+ if hasattr(self, "description"):
+ desc = self.description
+
+ function_name = self.meta["name"]
+ if hasattr(self, "function_name"):
+ function_name = self.function_name
+
+ return {
+ "type": "function",
+ "function": {
+ "name": function_name,
+ "description": desc,
+ "parameters": {
+ "type": "object",
+ "properties": params,
+ "required": [k for k, p in self.meta["parameters"].items() if p["required"]]
+ }
+ }
+ }
+
+
+class ToolBase(ComponentBase):
+ def __init__(self, canvas, id, param: ComponentParamBase):
+ from agent.canvas import Canvas # Local import to avoid cyclic dependency
+ assert isinstance(canvas, Canvas), "canvas must be an instance of Canvas"
+ self._canvas = canvas
+ self._id = id
+ self._param = param
+ self._param.check()
+
+ def get_meta(self) -> dict[str, Any]:
+ return self._param.get_meta()
+
+ def invoke(self, **kwargs):
+ self.set_output("_created_time", time.perf_counter())
+ try:
+ res = self._invoke(**kwargs)
+ except Exception as e:
+ self._param.outputs["_ERROR"] = {"value": str(e)}
+ logging.exception(e)
+ res = str(e)
+ self._param.debug_inputs = []
+
+ self.set_output("_elapsed_time", time.perf_counter() - self.output("_created_time"))
+ return res
+
+ def _retrieve_chunks(self, res_list: list, get_title, get_url, get_content, get_score=None):
+ chunks = []
+ aggs = []
+ for r in res_list:
+ content = get_content(r)
+ if not content:
+ continue
+ content = re.sub(r"!?\[[a-z]+\]\(data:image/png;base64,[ 0-9A-Za-z/_=+-]+\)", "", content)
+ content = content[:10000]
+ if not content:
+ continue
+ id = str(hash_str2int(content))
+ title = get_title(r)
+ url = get_url(r)
+ score = get_score(r) if get_score else 1
+ chunks.append({
+ "chunk_id": id,
+ "content": content,
+ "doc_id": id,
+ "docnm_kwd": title,
+ "similarity": score,
+ "url": url
+ })
+ aggs.append({
+ "doc_name": title,
+ "doc_id": id,
+ "count": 1,
+ "url": url
+ })
+ self._canvas.add_refernce(chunks, aggs)
+ self.set_output("formalized_content", "\n".join(kb_prompt({"chunks": chunks, "doc_aggs": aggs}, 200000, True)))
diff --git a/agent/tools/code_exec.py b/agent/tools/code_exec.py
new file mode 100644
index 000000000..191ca320e
--- /dev/null
+++ b/agent/tools/code_exec.py
@@ -0,0 +1,192 @@
+#
+# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import base64
+import logging
+import os
+from abc import ABC
+from enum import StrEnum
+from typing import Optional
+from pydantic import BaseModel, Field, field_validator
+from agent.tools.base import ToolParamBase, ToolBase, ToolMeta
+from api import settings
+from api.utils.api_utils import timeout
+
+
+class Language(StrEnum):
+ PYTHON = "python"
+ NODEJS = "nodejs"
+
+
+class CodeExecutionRequest(BaseModel):
+ code_b64: str = Field(..., description="Base64 encoded code string")
+ language: str = Field(default=Language.PYTHON.value, description="Programming language")
+ arguments: Optional[dict] = Field(default={}, description="Arguments")
+
+ @field_validator("code_b64")
+ @classmethod
+ def validate_base64(cls, v: str) -> str:
+ try:
+ base64.b64decode(v, validate=True)
+ return v
+ except Exception as e:
+ raise ValueError(f"Invalid base64 encoding: {str(e)}")
+
+ @field_validator("language", mode="before")
+ @classmethod
+ def normalize_language(cls, v) -> str:
+ if isinstance(v, str):
+ low = v.lower()
+ if low in ("python", "python3"):
+ return "python"
+ elif low in ("javascript", "nodejs"):
+ return "nodejs"
+ raise ValueError(f"Unsupported language: {v}")
+
+
+class CodeExecParam(ToolParamBase):
+ """
+ Define the code sandbox component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "execute_code",
+ "description": """
+This tool has a sandbox that can execute code written in 'Python'/'Javascript'. It recieves a piece of code and return a Json string.
+Here's a code example for Python(`main` function MUST be included):
+def main(arg1: str, arg2: str) -> dict:
+ return {
+ "result": arg1 + arg2,
+ }
+
+Here's a code example for Javascript(`main` function MUST be included and exported):
+const axios = require('axios');
+async function main(args) {
+ try {
+ const response = await axios.get('https://github.com/infiniflow/ragflow');
+ console.log('Body:', response.data);
+ } catch (error) {
+ console.error('Error:', error.message);
+ }
+}
+module.exports = { main };
+ """,
+ "parameters": {
+ "lang": {
+ "type": "string",
+ "description": "The programming language of this piece of code.",
+ "enum": ["python", "javascript"],
+ "required": True,
+ },
+ "script": {
+ "type": "string",
+ "description": "A piece of code in right format. There MUST be main function.",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.lang = Language.PYTHON.value
+ self.script = "def main(arg1: str, arg2: str) -> dict: return {\"result\": arg1 + arg2}"
+ self.arguments = {}
+ self.outputs = {"result": {"value": "", "type": "string"}}
+
+ def check(self):
+ self.check_valid_value(self.lang, "Support languages", ["python", "python3", "nodejs", "javascript"])
+ self.check_empty(self.script, "Script")
+
+ def get_input_form(self) -> dict[str, dict]:
+ res = {}
+ for k, v in self.arguments.items():
+ res[k] = {
+ "type": "line",
+ "name": k
+ }
+ return res
+
+
+class CodeExec(ToolBase, ABC):
+ component_name = "CodeExec"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))
+ def _invoke(self, **kwargs):
+ lang = kwargs.get("lang", self._param.lang)
+ script = kwargs.get("script", self._param.script)
+ arguments = {}
+ for k, v in self._param.arguments.items():
+ if kwargs.get(k):
+ arguments[k] = kwargs[k]
+ continue
+ arguments[k] = self._canvas.get_variable_value(v) if v else None
+
+ self._execute_code(
+ language=lang,
+ code=script,
+ arguments=arguments
+ )
+
+ def _execute_code(self, language: str, code: str, arguments: dict):
+ import requests
+
+ try:
+ code_b64 = self._encode_code(code)
+ code_req = CodeExecutionRequest(code_b64=code_b64, language=language, arguments=arguments).model_dump()
+ except Exception as e:
+ self.set_output("_ERROR", "construct code request error: " + str(e))
+
+ try:
+ resp = requests.post(url=f"http://{settings.SANDBOX_HOST}:9385/run", json=code_req, timeout=10)
+ logging.info(f"http://{settings.SANDBOX_HOST}:9385/run", code_req, resp.status_code)
+ if resp.status_code != 200:
+ resp.raise_for_status()
+ body = resp.json()
+ if body:
+ stderr = body.get("stderr")
+ if stderr:
+ self.set_output("_ERROR", stderr)
+ return
+ try:
+ rt = eval(body.get("stdout", ""))
+ except Exception:
+ rt = body.get("stdout", "")
+ logging.info(f"http://{settings.SANDBOX_HOST}:9385/run -> {rt}")
+ if isinstance(rt, tuple):
+ for i, (k, o) in enumerate(self._param.outputs.items()):
+ if k.find("_") == 0:
+ continue
+ o["value"] = rt[i]
+ elif isinstance(rt, dict):
+ for i, (k, o) in enumerate(self._param.outputs.items()):
+ if k not in rt or k.find("_") == 0:
+ continue
+ o["value"] = rt[k]
+ else:
+ for i, (k, o) in enumerate(self._param.outputs.items()):
+ if k.find("_") == 0:
+ continue
+ o["value"] = rt
+ else:
+ self.set_output("_ERROR", "There is no response from sandbox")
+
+ except Exception as e:
+ self.set_output("_ERROR", "Exception executing code: " + str(e))
+
+ return self.output()
+
+ def _encode_code(self, code: str) -> str:
+ return base64.b64encode(code.encode("utf-8")).decode("utf-8")
+
+
diff --git a/agent/component/crawler.py b/agent/tools/crawler.py
similarity index 93%
rename from agent/component/crawler.py
rename to agent/tools/crawler.py
index d8c5381b1..b4c4f6717 100644
--- a/agent/component/crawler.py
+++ b/agent/tools/crawler.py
@@ -16,11 +16,12 @@
from abc import ABC
import asyncio
from crawl4ai import AsyncWebCrawler
-from agent.component.base import ComponentBase, ComponentParamBase
+
+from agent.tools.base import ToolParamBase, ToolBase
from api.utils.web_utils import is_valid_url
-class CrawlerParam(ComponentParamBase):
+class CrawlerParam(ToolParamBase):
"""
Define the Crawler component parameters.
"""
@@ -34,7 +35,7 @@ class CrawlerParam(ComponentParamBase):
self.check_valid_value(self.extract_type, "Type of content from the crawler", ['html', 'markdown', 'content'])
-class Crawler(ComponentBase, ABC):
+class Crawler(ToolBase, ABC):
component_name = "Crawler"
def _run(self, history, **kwargs):
diff --git a/agent/component/deepl.py b/agent/tools/deepl.py
similarity index 100%
rename from agent/component/deepl.py
rename to agent/tools/deepl.py
diff --git a/agent/tools/duckduckgo.py b/agent/tools/duckduckgo.py
new file mode 100644
index 000000000..da903c069
--- /dev/null
+++ b/agent/tools/duckduckgo.py
@@ -0,0 +1,114 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+from duckduckgo_search import DDGS
+from agent.tools.base import ToolMeta, ToolParamBase, ToolBase
+from api.utils.api_utils import timeout
+
+
+class DuckDuckGoParam(ToolParamBase):
+ """
+ Define the DuckDuckGo component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "duckduckgo_search",
+ "description": "DuckDuckGo is a search engine focused on privacy. It offers search capabilities for web pages, images, and provides translation services. DuckDuckGo also features a private AI chat interface, providing users with an AI assistant that prioritizes data protection.",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keywords to execute with DuckDuckGo. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ },
+ "channel": {
+ "type": "string",
+ "description": "default:general. The category of the search. `news` is useful for retrieving real-time updates, particularly about politics, sports, and major current events covered by mainstream media sources. `general` is for broader, more general-purpose searches that may include a wide range of sources.",
+ "enum": ["general", "news"],
+ "default": "general",
+ "required": False,
+ },
+ }
+ }
+ super().__init__()
+ self.top_n = 10
+ self.channel = "text"
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+ self.check_valid_value(self.channel, "Web Search or News", ["text", "news"])
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ },
+ "channel": {
+ "name": "Channel",
+ "type": "options",
+ "value": "general",
+ "options": ["general", "news"]
+ }
+ }
+
+
+class DuckDuckGo(ToolBase, ABC):
+ component_name = "DuckDuckGo"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ if kwargs.get("topic", "general") == "general":
+ with DDGS() as ddgs:
+ # {'title': '', 'href': '', 'body': ''}
+ duck_res = ddgs.text(kwargs["query"], max_results=self._param.top_n)
+ self._retrieve_chunks(duck_res,
+ get_title=lambda r: r["title"],
+ get_url=lambda r: r.get("href", r.get("url")),
+ get_content=lambda r: r["body"])
+ self.set_output("json", duck_res)
+ return self.output("formalized_content")
+ else:
+ with DDGS() as ddgs:
+ # {'date': '', 'title': '', 'body': '', 'url': '', 'image': '', 'source': ''}
+ duck_res = ddgs.news(kwargs["query"], max_results=self._param.top_n)
+ self._retrieve_chunks(duck_res,
+ get_title=lambda r: r["title"],
+ get_url=lambda r: r.get("href", r.get("url")),
+ get_content=lambda r: r["body"])
+ self.set_output("json", duck_res)
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"DuckDuckGo error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"DuckDuckGo error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/tools/email.py b/agent/tools/email.py
new file mode 100644
index 000000000..89a3113c3
--- /dev/null
+++ b/agent/tools/email.py
@@ -0,0 +1,207 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+import time
+from abc import ABC
+import json
+import smtplib
+import logging
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from email.header import Header
+from email.utils import formataddr
+
+from agent.tools.base import ToolParamBase, ToolBase, ToolMeta
+from api.utils.api_utils import timeout
+
+
+class EmailParam(ToolParamBase):
+ """
+ Define the Email component parameters.
+ """
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "email",
+ "description": "The email is a method of electronic communication for sending and receiving information through the Internet. This tool helps users to send emails to one person or to multiple recipients with support for CC, BCC, file attachments, and markdown-to-HTML conversion.",
+ "parameters": {
+ "to_email": {
+ "type": "string",
+ "description": "The target email address.",
+ "default": "{sys.query}",
+ "required": True
+ },
+ "cc_email": {
+ "type": "string",
+ "description": "The other email addresses needs to be send to. Comma splited.",
+ "default": "",
+ "required": False
+ },
+ "content": {
+ "type": "string",
+ "description": "The content of the email.",
+ "default": "",
+ "required": False
+ },
+ "subject": {
+ "type": "string",
+ "description": "The subject/title of the email.",
+ "default": "",
+ "required": False
+ }
+ }
+ }
+ super().__init__()
+ # Fixed configuration parameters
+ self.smtp_server = "" # SMTP server address
+ self.smtp_port = 465 # SMTP port
+ self.email = "" # Sender email
+ self.password = "" # Email authorization code
+ self.sender_name = "" # Sender name
+
+ def check(self):
+ # Check required parameters
+ self.check_empty(self.smtp_server, "SMTP Server")
+ self.check_empty(self.email, "Email")
+ self.check_empty(self.password, "Password")
+ self.check_empty(self.sender_name, "Sender Name")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "to_email": {
+ "name": "To ",
+ "type": "line"
+ },
+ "subject": {
+ "name": "Subject",
+ "type": "line",
+ "optional": True
+ },
+ "cc_email": {
+ "name": "CC To",
+ "type": "line",
+ "optional": True
+ },
+ }
+
+class Email(ToolBase, ABC):
+ component_name = "Email"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("to_email"):
+ self.set_output("success", False)
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ # Parse JSON string passed from upstream
+ email_data = kwargs
+
+ # Validate required fields
+ if "to_email" not in email_data:
+ return Email.be_output("Missing required field: to_email")
+
+ # Create email object
+ msg = MIMEMultipart('alternative')
+
+ # Properly handle sender name encoding
+ msg['From'] = formataddr((str(Header(self._param.sender_name,'utf-8')), self._param.email))
+ msg['To'] = email_data["to_email"]
+ if email_data.get("cc_email"):
+ msg['Cc'] = email_data["cc_email"]
+ msg['Subject'] = Header(email_data.get("subject", "No Subject"), 'utf-8').encode()
+
+ # Use content from email_data or default content
+ email_content = email_data.get("content", "No content provided")
+ # msg.attach(MIMEText(email_content, 'plain', 'utf-8'))
+ msg.attach(MIMEText(email_content, 'html', 'utf-8'))
+
+ # Connect to SMTP server and send
+ logging.info(f"Connecting to SMTP server {self._param.smtp_server}:{self._param.smtp_port}")
+
+ context = smtplib.ssl.create_default_context()
+ with smtplib.SMTP(self._param.smtp_server, self._param.smtp_port) as server:
+ server.ehlo()
+ server.starttls(context=context)
+ server.ehlo()
+ # Login
+ logging.info(f"Attempting to login with email: {self._param.email}")
+ server.login(self._param.email, self._param.password)
+
+ # Get all recipient list
+ recipients = [email_data["to_email"]]
+ if email_data.get("cc_email"):
+ recipients.extend(email_data["cc_email"].split(','))
+
+ # Send email
+ logging.info(f"Sending email to recipients: {recipients}")
+ try:
+ server.send_message(msg, self._param.email, recipients)
+ success = True
+ except Exception as e:
+ logging.error(f"Error during send_message: {str(e)}")
+ # Try alternative method
+ server.sendmail(self._param.email, recipients, msg.as_string())
+ success = True
+
+ try:
+ server.quit()
+ except Exception as e:
+ # Ignore errors when closing connection
+ logging.warning(f"Non-fatal error during connection close: {str(e)}")
+
+ self.set_output("success", success)
+ return success
+
+ except json.JSONDecodeError:
+ error_msg = "Invalid JSON format in input"
+ logging.error(error_msg)
+ self.set_output("_ERROR", error_msg)
+ self.set_output("success", False)
+ return False
+
+ except smtplib.SMTPAuthenticationError:
+ error_msg = "SMTP Authentication failed. Please check your email and authorization code."
+ logging.error(error_msg)
+ self.set_output("_ERROR", error_msg)
+ self.set_output("success", False)
+ return False
+
+ except smtplib.SMTPConnectError:
+ error_msg = f"Failed to connect to SMTP server {self._param.smtp_server}:{self._param.smtp_port}"
+ logging.error(error_msg)
+ last_e = error_msg
+ time.sleep(self._param.delay_after_error)
+
+ except smtplib.SMTPException as e:
+ error_msg = f"SMTP error occurred: {str(e)}"
+ logging.error(error_msg)
+ last_e = error_msg
+ time.sleep(self._param.delay_after_error)
+
+ except Exception as e:
+ error_msg = f"Unexpected error: {str(e)}"
+ logging.error(error_msg)
+ self.set_output("_ERROR", error_msg)
+ self.set_output("success", False)
+ return False
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return False
+
+ assert False, self.output()
\ No newline at end of file
diff --git a/agent/tools/exesql.py b/agent/tools/exesql.py
new file mode 100644
index 000000000..087abcc1d
--- /dev/null
+++ b/agent/tools/exesql.py
@@ -0,0 +1,133 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+from abc import ABC
+import pandas as pd
+import pymysql
+import psycopg2
+import pyodbc
+from agent.tools.base import ToolParamBase, ToolBase, ToolMeta
+from api.utils.api_utils import timeout
+
+
+class ExeSQLParam(ToolParamBase):
+ """
+ Define the ExeSQL component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "execute_sql",
+ "description": "This is a tool that can execute SQL.",
+ "parameters": {
+ "sql": {
+ "type": "string",
+ "description": "The SQL needs to be executed.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.db_type = "mysql"
+ self.database = ""
+ self.username = ""
+ self.host = ""
+ self.port = 3306
+ self.password = ""
+ self.max_records = 1024
+
+ def check(self):
+ self.check_valid_value(self.db_type, "Choose DB type", ['mysql', 'postgresql', 'mariadb', 'mssql'])
+ self.check_empty(self.database, "Database name")
+ self.check_empty(self.username, "database username")
+ self.check_empty(self.host, "IP Address")
+ self.check_positive_integer(self.port, "IP Port")
+ self.check_empty(self.password, "Database password")
+ self.check_positive_integer(self.max_records, "Maximum number of records")
+ if self.database == "rag_flow":
+ if self.host == "ragflow-mysql":
+ raise ValueError("For the security reason, it dose not support database named rag_flow.")
+ if self.password == "infini_rag_flow":
+ raise ValueError("For the security reason, it dose not support database named rag_flow.")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "sql": {
+ "name": "SQL",
+ "type": "line"
+ }
+ }
+
+
+class ExeSQL(ToolBase, ABC):
+ component_name = "ExeSQL"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))
+ def _invoke(self, **kwargs):
+ sql = kwargs.get("sql")
+ if not sql:
+ raise Exception("SQL for `ExeSQL` MUST not be empty.")
+ sqls = sql.split(";")
+
+ if self._param.db_type in ["mysql", "mariadb"]:
+ db = pymysql.connect(db=self._param.database, user=self._param.username, host=self._param.host,
+ port=self._param.port, password=self._param.password)
+ elif self._param.db_type == 'postgresql':
+ db = psycopg2.connect(dbname=self._param.database, user=self._param.username, host=self._param.host,
+ port=self._param.port, password=self._param.password)
+ elif self._param.db_type == 'mssql':
+ conn_str = (
+ r'DRIVER={ODBC Driver 17 for SQL Server};'
+ r'SERVER=' + self._param.host + ',' + str(self._param.port) + ';'
+ r'DATABASE=' + self._param.database + ';'
+ r'UID=' + self._param.username + ';'
+ r'PWD=' + self._param.password
+ )
+ db = pyodbc.connect(conn_str)
+ try:
+ cursor = db.cursor()
+ except Exception as e:
+ raise Exception("Database Connection Failed! \n" + str(e))
+
+ sql_res = []
+ formalized_content = []
+ for single_sql in sqls:
+ single_sql = single_sql.replace('```','')
+ if not single_sql:
+ continue
+
+ cursor.execute(single_sql)
+ if cursor.rowcount == 0:
+ sql_res.append({"content": "No record in the database!"})
+ break
+ if self._param.db_type == 'mssql':
+ single_res = pd.DataFrame.from_records(cursor.fetchmany(self._param.max_records),
+ columns=[desc[0] for desc in cursor.description])
+ else:
+ single_res = pd.DataFrame([i for i in cursor.fetchmany(self._param.max_records)])
+ single_res.columns = [i[0] for i in cursor.description]
+
+ sql_res.append(single_res.to_dict(orient='records'))
+ formalized_content.append(single_res.to_markdown(index=False, floatfmt=".6f"))
+
+ self.set_output("json", sql_res)
+ self.set_output("formalized_content", "\n\n".join(formalized_content))
+ return self.output("formalized_content")
+
+
+ def debug(self, **kwargs):
+ return self._run([], **kwargs)
diff --git a/agent/tools/github.py b/agent/tools/github.py
new file mode 100644
index 000000000..385b88b02
--- /dev/null
+++ b/agent/tools/github.py
@@ -0,0 +1,88 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+import requests
+from agent.tools.base import ToolParamBase, ToolMeta, ToolBase
+from api.utils.api_utils import timeout
+
+
+class GitHubParam(ToolParamBase):
+ """
+ Define the GitHub component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "github_search",
+ "description": """GitHub repository search is a feature that enables users to find specific repositories on the GitHub platform. This search functionality allows users to locate projects, codebases, and other content hosted on GitHub based on various criteria.""",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keywords to execute with GitHub. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 10
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class GitHub(ToolBase, ABC):
+ component_name = "GitHub"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ url = 'https://api.github.com/search/repositories?q=' + kwargs["query"] + '&sort=stars&order=desc&per_page=' + str(
+ self._param.top_n)
+ headers = {"Content-Type": "application/vnd.github+json", "X-GitHub-Api-Version": '2022-11-28'}
+ response = requests.get(url=url, headers=headers).json()
+ self._retrieve_chunks(response['items'],
+ get_title=lambda r: r["name"],
+ get_url=lambda r: r["html_url"],
+ get_content=lambda r: str(r["description"]) + '\n stars:' + str(r['watchers']))
+ self.set_output("json", response['items'])
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"GitHub error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"GitHub error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/component/google.py b/agent/tools/google.py
similarity index 53%
rename from agent/component/google.py
rename to agent/tools/google.py
index 691dc2397..1d1bd86f7 100644
--- a/agent/component/google.py
+++ b/agent/tools/google.py
@@ -14,26 +14,52 @@
# limitations under the License.
#
import logging
+import os
+import time
from abc import ABC
from serpapi import GoogleSearch
-import pandas as pd
-from agent.component.base import ComponentBase, ComponentParamBase
+from agent.tools.base import ToolParamBase, ToolMeta, ToolBase
+from api.utils.api_utils import timeout
-class GoogleParam(ComponentParamBase):
+class GoogleParam(ToolParamBase):
"""
Define the Google component parameters.
"""
def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "google_search",
+ "description": """Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking ...""",
+ "parameters": {
+ "q": {
+ "type": "string",
+ "description": "The search keywords to execute with Google. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ },
+ "start": {
+ "type": "integer",
+ "description": "Parameter defines the result offset. It skips the given number of results. It's used for pagination. (e.g., 0 (default) is the first page of results, 10 is the 2nd page of results, 20 is the 3rd page of results, etc.). Google Local Results only accepts multiples of 20(e.g. 20 for the second page results, 40 for the third page results, etc.) as the `start` value.",
+ "default": "0",
+ "required": False,
+ },
+ "num": {
+ "type": "integer",
+ "description": "Parameter defines the maximum number of results to return. (e.g., 10 (default) returns 10 results, 40 returns 40 results, and 100 returns 100 results). The use of num may introduce latency, and/or prevent the inclusion of specialized result types. It is better to omit this parameter unless it is strictly necessary to increase the number of results per page. Results are not guaranteed to have the number of results specified in num.",
+ "default": "6",
+ "required": False,
+ }
+ }
+ }
super().__init__()
- self.top_n = 10
- self.api_key = "xxx"
+ self.start = 0
+ self.num = 6
+ self.api_key = ""
self.country = "cn"
self.language = "en"
def check(self):
- self.check_positive_integer(self.top_n, "Top N")
self.check_empty(self.api_key, "SerpApi API key")
self.check_valid_value(self.country, "Google Country",
['af', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at',
@@ -69,28 +95,60 @@ class GoogleParam(ComponentParamBase):
'ug', 'uk', 'ur', 'uz', 'vu', 'vi', 'cy', 'wo', 'xh', 'yi', 'yo', 'zu']
)
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "q": {
+ "name": "Query",
+ "type": "line"
+ },
+ "start": {
+ "name": "From",
+ "type": "integer",
+ "value": 0
+ },
+ "num": {
+ "name": "Limit",
+ "type": "integer",
+ "value": 12
+ }
+ }
-class Google(ComponentBase, ABC):
+class Google(ToolBase, ABC):
component_name = "Google"
- def _run(self, history, **kwargs):
- ans = self.get_input()
- ans = " - ".join(ans["content"]) if "content" in ans else ""
- if not ans:
- return Google.be_output("")
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("q"):
+ self.set_output("formalized_content", "")
+ return ""
- try:
- client = GoogleSearch(
- {"engine": "google", "q": ans, "api_key": self._param.api_key, "gl": self._param.country,
- "hl": self._param.language, "num": self._param.top_n})
- google_res = [{"content": '' + i["title"] + ' ' + i["snippet"]} for i in
- client.get_dict()["organic_results"]]
- except Exception:
- return Google.be_output("**ERROR**: Existing Unavailable Parameters!")
+ params = {
+ "api_key": self._param.api_key,
+ "engine": "google",
+ "q": kwargs["q"],
+ "google_domain": "google.com",
+ "gl": self._param.country,
+ "hl": self._param.language
+ }
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ search = GoogleSearch(params).get_dict()
+ self._retrieve_chunks(search["organic_results"],
+ get_title=lambda r: r["title"],
+ get_url=lambda r: r["link"],
+ get_content=lambda r: r.get("about_this_result", {}).get("source", {}).get("description", r["snippet"])
+ )
+ self.set_output("json", search["organic_results"])
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"Google error: {e}")
+ time.sleep(self._param.delay_after_error)
- if not google_res:
- return Google.be_output("")
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"Google error: {last_e}"
+
+ assert False, self.output()
- df = pd.DataFrame(google_res)
- logging.debug(f"df: {df}")
- return df
diff --git a/agent/tools/googlescholar.py b/agent/tools/googlescholar.py
new file mode 100644
index 000000000..d26a68a9c
--- /dev/null
+++ b/agent/tools/googlescholar.py
@@ -0,0 +1,93 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+from scholarly import scholarly
+from agent.tools.base import ToolMeta, ToolParamBase, ToolBase
+from api.utils.api_utils import timeout
+
+
+class GoogleScholarParam(ToolParamBase):
+ """
+ Define the GoogleScholar component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "google_scholar_search",
+ "description": """Google Scholar provides a simple way to broadly search for scholarly literature. From one place, you can search across many disciplines and sources: articles, theses, books, abstracts and court opinions, from academic publishers, professional societies, online repositories, universities and other web sites. Google Scholar helps you find relevant work across the world of scholarly research.""",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keyword to execute with Google Scholar. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 12
+ self.sort_by = 'relevance'
+ self.year_low = None
+ self.year_high = None
+ self.patents = True
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+ self.check_valid_value(self.sort_by, "GoogleScholar Sort_by", ['date', 'relevance'])
+ self.check_boolean(self.patents, "Whether or not to include patents, defaults to True")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class GoogleScholar(ToolBase, ABC):
+ component_name = "GoogleScholar"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ scholar_client = scholarly.search_pubs(kwargs["query"], patents=self._param.patents, year_low=self._param.year_low,
+ year_high=self._param.year_high, sort_by=self._param.sort_by)
+ self._retrieve_chunks(scholar_client,
+ get_title=lambda r: r['bib']['title'],
+ get_url=lambda r: r["pub_url"],
+ get_content=lambda r: "\n author: " + ",".join(r['bib']['author']) + '\n Abstract: ' + r['bib'].get('abstract', 'no abstract')
+ )
+ self.set_output("json", list(scholar_client))
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"GoogleScholar error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"GoogleScholar error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/component/jin10.py b/agent/tools/jin10.py
similarity index 100%
rename from agent/component/jin10.py
rename to agent/tools/jin10.py
diff --git a/agent/tools/pubmed.py b/agent/tools/pubmed.py
new file mode 100644
index 000000000..8af7c1435
--- /dev/null
+++ b/agent/tools/pubmed.py
@@ -0,0 +1,105 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+from Bio import Entrez
+import re
+import xml.etree.ElementTree as ET
+from agent.tools.base import ToolParamBase, ToolMeta, ToolBase
+from api.utils.api_utils import timeout
+
+
+class PubMedParam(ToolParamBase):
+ """
+ Define the PubMed component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "pubmed_search",
+ "description": """
+PubMed is an openly accessible, free database which includes primarily the MEDLINE database of references and abstracts on life sciences and biomedical topics.
+In addition to MEDLINE, PubMed provides access to:
+ - older references from the print version of Index Medicus, back to 1951 and earlier
+ - references to some journals before they were indexed in Index Medicus and MEDLINE, for instance Science, BMJ, and Annals of Surgery
+ - very recent entries to records for an article before it is indexed with Medical Subject Headings (MeSH) and added to MEDLINE
+ - a collection of books available full-text and other subsets of NLM records[4]
+ - PMC citations
+ - NCBI Bookshelf
+ """,
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keywords to execute with PubMed. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 12
+ self.email = "A.N.Other@example.com"
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class PubMed(ToolBase, ABC):
+ component_name = "PubMed"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ Entrez.email = self._param.email
+ pubmedids = Entrez.read(Entrez.esearch(db='pubmed', retmax=self._param.top_n, term=kwargs["query"]))['IdList']
+ pubmedcnt = ET.fromstring(re.sub(r'<(/?)b>|<(/?)i>', '', Entrez.efetch(db='pubmed', id=",".join(pubmedids),
+ retmode="xml").read().decode("utf-8")))
+ self._retrieve_chunks(pubmedcnt.findall("PubmedArticle"),
+ get_title=lambda child: child.find("MedlineCitation").find("Article").find("ArticleTitle").text,
+ get_url=lambda child: "https://pubmed.ncbi.nlm.nih.gov/" + child.find("MedlineCitation").find("PMID").text,
+ get_content=lambda child: child.find("MedlineCitation") \
+ .find("Article") \
+ .find("Abstract") \
+ .find("AbstractText").text \
+ if child.find("MedlineCitation")\
+ .find("Article").find("Abstract") \
+ else "No abstract available")
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"PubMed error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"PubMed error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/component/qweather.py b/agent/tools/qweather.py
similarity index 100%
rename from agent/component/qweather.py
rename to agent/tools/qweather.py
diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py
new file mode 100644
index 000000000..d25061f60
--- /dev/null
+++ b/agent/tools/retrieval.py
@@ -0,0 +1,161 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+import re
+from abc import ABC
+from agent.tools.base import ToolParamBase, ToolBase, ToolMeta
+from api.db import LLMType
+from api.db.services.knowledgebase_service import KnowledgebaseService
+from api.db.services.llm_service import LLMBundle
+from api import settings
+from api.utils.api_utils import timeout
+from rag.app.tag import label_question
+from rag.prompts import kb_prompt
+from rag.prompts.prompts import cross_languages
+
+
+class RetrievalParam(ToolParamBase):
+ """
+ Define the Retrieval component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "search_my_dateset",
+ "description": "This tool can be utilized for relevant content searching in the datasets.",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The keywords to search the dataset. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.function_name = "search_my_dateset"
+ self.description = "This tool can be utilized for relevant content searching in the datasets."
+ self.similarity_threshold = 0.2
+ self.keywords_similarity_weight = 0.5
+ self.top_n = 8
+ self.top_k = 1024
+ self.kb_ids = []
+ self.kb_vars = []
+ self.rerank_id = ""
+ self.empty_response = ""
+ self.use_kg = False
+ self.cross_languages = []
+
+ def check(self):
+ self.check_decimal_float(self.similarity_threshold, "[Retrieval] Similarity threshold")
+ self.check_decimal_float(self.keywords_similarity_weight, "[Retrieval] Keyword similarity weight")
+ self.check_positive_number(self.top_n, "[Retrieval] Top N")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class Retrieval(ToolBase, ABC):
+ component_name = "Retrieval"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", self._param.empty_response)
+
+ kb_ids: list[str] = []
+ for id in self._param.kb_ids:
+ if id.find("@") < 0:
+ kb_ids.append(id)
+ continue
+ kb_nm = self._canvas.get_variable_value(id)
+ e, kb = KnowledgebaseService.get_by_name(kb_nm)
+ if not e:
+ raise Exception(f"Dataset({kb_nm}) does not exist.")
+ kb_ids.append(kb.id)
+
+ filtered_kb_ids: list[str] = list(set([kb_id for kb_id in kb_ids if kb_id]))
+
+ kbs = KnowledgebaseService.get_by_ids(filtered_kb_ids)
+ if not kbs:
+ raise Exception("No dataset is selected.")
+
+ embd_nms = list(set([kb.embd_id for kb in kbs]))
+ assert len(embd_nms) == 1, "Knowledge bases use different embedding models."
+
+ embd_mdl = None
+ if embd_nms:
+ embd_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.EMBEDDING, embd_nms[0])
+
+ rerank_mdl = None
+ if self._param.rerank_id:
+ rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id)
+
+ query = kwargs["query"]
+ if self._param.cross_languages:
+ query = cross_languages(kbs[0].tenant_id, None, query, self._param.cross_languages)
+
+ if kbs:
+ query = re.sub(r"^user[::\s]*", "", query, flags=re.IGNORECASE)
+ kbinfos = settings.retrievaler.retrieval(
+ query,
+ embd_mdl,
+ [kb.tenant_id for kb in kbs],
+ filtered_kb_ids,
+ 1,
+ self._param.top_n,
+ self._param.similarity_threshold,
+ 1 - self._param.keywords_similarity_weight,
+ aggs=False,
+ rerank_mdl=rerank_mdl,
+ rank_feature=label_question(query, kbs),
+ )
+ if self._param.use_kg:
+ ck = settings.kg_retrievaler.retrieval(query,
+ [kb.tenant_id for kb in kbs],
+ kb_ids,
+ embd_mdl,
+ LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT))
+ if ck["content_with_weight"]:
+ kbinfos["chunks"].insert(0, ck)
+ else:
+ kbinfos = {"chunks": [], "doc_aggs": []}
+
+ if self._param.use_kg and kbs:
+ ck = settings.kg_retrievaler.retrieval(query, [kb.tenant_id for kb in kbs], filtered_kb_ids, embd_mdl, LLMBundle(kbs[0].tenant_id, LLMType.CHAT))
+ if ck["content_with_weight"]:
+ ck["content"] = ck["content_with_weight"]
+ del ck["content_with_weight"]
+ kbinfos["chunks"].insert(0, ck)
+
+ for ck in kbinfos["chunks"]:
+ if "vector" in ck:
+ del ck["vector"]
+ if "content_ltks" in ck:
+ del ck["content_ltks"]
+
+ if not kbinfos["chunks"]:
+ self.set_output("formalized_content", self._param.empty_response)
+ return
+
+ self._canvas.add_refernce(kbinfos["chunks"], kbinfos["doc_aggs"])
+ form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True))
+ self.set_output("formalized_content", form_cnt)
+ return form_cnt
diff --git a/agent/tools/tavily.py b/agent/tools/tavily.py
new file mode 100644
index 000000000..8072d3eb3
--- /dev/null
+++ b/agent/tools/tavily.py
@@ -0,0 +1,218 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+from tavily import TavilyClient
+from agent.tools.base import ToolParamBase, ToolBase, ToolMeta
+from api.utils.api_utils import timeout
+
+
+class TavilySearchParam(ToolParamBase):
+ """
+ Define the Retrieval component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "tavily_search",
+ "description": """
+Tavily is a search engine optimized for LLMs, aimed at efficient, quick and persistent search results.
+When searching:
+ - Start with specific query which should focus on just a single aspect.
+ - Number of keywords in query should be less than 5.
+ - Broaden search terms if needed
+ - Cross-reference information from multiple sources
+ """,
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keywords to execute with Tavily. The keywords should be the most important words/terms(includes synonyms) from the original request.",
+ "default": "{sys.query}",
+ "required": True
+ },
+ "topic": {
+ "type": "string",
+ "description": "default:general. The category of the search.news is useful for retrieving real-time updates, particularly about politics, sports, and major current events covered by mainstream media sources. general is for broader, more general-purpose searches that may include a wide range of sources.",
+ "enum": ["general", "news"],
+ "default": "general",
+ "required": False,
+ },
+ "include_domains": {
+ "type": "array",
+ "description": "default:[]. A list of domains only from which the search results can be included.",
+ "default": [],
+ "items": {
+ "type": "string",
+ "description": "Domain name that must be included, e.g. www.yahoo.com"
+ },
+ "required": False
+ },
+ "exclude_domains": {
+ "type": "array",
+ "description": "default:[]. A list of domains from which the search results can not be included",
+ "default": [],
+ "items": {
+ "type": "string",
+ "description": "Domain name that must be excluded, e.g. www.yahoo.com"
+ },
+ "required": False
+ },
+ }
+ }
+ super().__init__()
+ self.api_key = ""
+ self.search_depth = "basic" # basic/advanced
+ self.max_results = 6
+ self.days = 14
+ self.include_answer = False
+ self.include_raw_content = False
+ self.include_images = False
+ self.include_image_descriptions = False
+
+ def check(self):
+ self.check_valid_value(self.topic, "Tavily topic: should be in 'general/news'", ["general", "news"])
+ self.check_valid_value(self.search_depth, "Tavily search depth should be in 'basic/advanced'", ["basic", "advanced"])
+ self.check_positive_integer(self.max_results, "Tavily max result number should be within [1, 20]")
+ self.check_positive_integer(self.days, "Tavily days should be greater than 1")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class TavilySearch(ToolBase, ABC):
+ component_name = "TavilySearch"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ self.tavily_client = TavilyClient(api_key=self._param.api_key)
+ last_e = None
+ for fld in ["search_depth", "topic", "max_results", "days", "include_answer", "include_raw_content", "include_images", "include_image_descriptions", "include_domains", "exclude_domains"]:
+ if fld not in kwargs:
+ kwargs[fld] = getattr(self._param, fld)
+ for _ in range(self._param.max_retries+1):
+ try:
+ kwargs["include_images"] = False
+ kwargs["include_raw_content"] = False
+ res = self.tavily_client.search(**kwargs)
+ self._retrieve_chunks(res["results"],
+ get_title=lambda r: r["title"],
+ get_url=lambda r: r["url"],
+ get_content=lambda r: r["raw_content"] if r["raw_content"] else r["content"],
+ get_score=lambda r: r["score"])
+ self.set_output("json", res["results"])
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"Tavily error: {e}")
+ time.sleep(self._param.delay_after_error)
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"Tavily error: {last_e}"
+
+ assert False, self.output()
+
+
+class TavilyExtractParam(ToolParamBase):
+ """
+ Define the Retrieval component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "tavily_extract",
+ "description": "Extract web page content from one or more specified URLs using Tavily Extract.",
+ "parameters": {
+ "urls": {
+ "type": "array",
+ "description": "The URLs to extract content from.",
+ "default": "",
+ "items": {
+ "type": "string",
+ "description": "The URL to extract content from, e.g. www.yahoo.com"
+ },
+ "required": True
+ },
+ "extract_depth": {
+ "type": "string",
+ "description": "The depth of the extraction process. advanced extraction retrieves more data, including tables and embedded content, with higher success but may increase latency.basic extraction costs 1 credit per 5 successful URL extractions, while advanced extraction costs 2 credits per 5 successful URL extractions.",
+ "enum": ["basic", "advanced"],
+ "default": "basic",
+ "required": False,
+ },
+ "format": {
+ "type": "string",
+ "description": "The format of the extracted web page content. markdown returns content in markdown format. text returns plain text and may increase latency.",
+ "enum": ["markdown", "text"],
+ "default": "markdown",
+ "required": False,
+ }
+ }
+ }
+ super().__init__()
+ self.api_key = ""
+ self.extract_depth = "basic" # basic/advanced
+ self.urls = []
+ self.format = "markdown"
+ self.include_images = False
+
+ def check(self):
+ self.check_valid_value(self.extract_depth, "Tavily extract depth should be in 'basic/advanced'", ["basic", "advanced"])
+ self.check_valid_value(self.format, "Tavily extract format should be in 'markdown/text'", ["markdown", "text"])
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "urls": {
+ "name": "URLs",
+ "type": "line"
+ }
+ }
+
+class TavilyExtract(ToolBase, ABC):
+ component_name = "TavilyExtract"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))
+ def _invoke(self, **kwargs):
+ self.tavily_client = TavilyClient(api_key=self._param.api_key)
+ last_e = None
+ for fld in ["urls", "extract_depth", "format"]:
+ if fld not in kwargs:
+ kwargs[fld] = getattr(self._param, fld)
+ if kwargs.get("urls") and isinstance(kwargs["urls"], str):
+ kwargs["urls"] = kwargs["urls"].split(",")
+ for _ in range(self._param.max_retries+1):
+ try:
+ kwargs["include_images"] = False
+ res = self.tavily_client.extract(**kwargs)
+ self.set_output("json", res["results"])
+ return self.output("json")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"Tavily error: {e}")
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"Tavily error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/component/tushare.py b/agent/tools/tushare.py
similarity index 100%
rename from agent/component/tushare.py
rename to agent/tools/tushare.py
diff --git a/agent/tools/wencai.py b/agent/tools/wencai.py
new file mode 100644
index 000000000..b751b92d9
--- /dev/null
+++ b/agent/tools/wencai.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+import pandas as pd
+import pywencai
+
+from agent.tools.base import ToolParamBase, ToolMeta, ToolBase
+from api.utils.api_utils import timeout
+
+
+class WenCaiParam(ToolParamBase):
+ """
+ Define the WenCai component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "iwencai",
+ "description": """
+iwencai search: search platform is committed to providing hundreds of millions of investors with the most timely, accurate and comprehensive information, covering news, announcements, research reports, blogs, forums, Weibo, characters, etc.
+robo-advisor intelligent stock selection platform: through AI technology, is committed to providing investors with intelligent stock selection, quantitative investment, main force tracking, value investment, technical analysis and other types of stock selection technologies.
+fund selection platform: through AI technology, is committed to providing excellent fund, value investment, quantitative analysis and other fund selection technologies for foundation citizens.
+""",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The question/conditions to select stocks.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 10
+ self.query_type = "stock"
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+ self.check_valid_value(self.query_type, "Query type",
+ ['stock', 'zhishu', 'fund', 'hkstock', 'usstock', 'threeboard', 'conbond', 'insurance',
+ 'futures', 'lccp',
+ 'foreign_exchange'])
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class WenCai(ToolBase, ABC):
+ component_name = "WenCai"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("report", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ wencai_res = []
+ res = pywencai.get(query=kwargs["query"], query_type=self._param.query_type, perpage=self._param.top_n)
+ if isinstance(res, pd.DataFrame):
+ wencai_res.append(res.to_markdown())
+ elif isinstance(res, dict):
+ for item in res.items():
+ if isinstance(item[1], list):
+ wencai_res.append(item[0] + "\n" + pd.DataFrame(item[1]).to_markdown())
+ elif isinstance(item[1], str):
+ wencai_res.append(item[0] + "\n" + item[1])
+ elif isinstance(item[1], dict):
+ if "meta" in item[1].keys():
+ continue
+ wencai_res.append(pd.DataFrame.from_dict(item[1], orient='index').to_markdown())
+ elif isinstance(item[1], pd.DataFrame):
+ if "image_url" in item[1].columns:
+ continue
+ wencai_res.append(item[1].to_markdown())
+ else:
+ wencai_res.append(item[0] + "\n" + str(item[1]))
+ self.set_output("report", "\n\n".join(wencai_res))
+ return self.output("report")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"WenCai error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"WenCai error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/tools/wikipedia.py b/agent/tools/wikipedia.py
new file mode 100644
index 000000000..d073fb7d8
--- /dev/null
+++ b/agent/tools/wikipedia.py
@@ -0,0 +1,98 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+import wikipedia
+from agent.tools.base import ToolMeta, ToolParamBase, ToolBase
+from api.utils.api_utils import timeout
+
+
+class WikipediaParam(ToolParamBase):
+ """
+ Define the Wikipedia component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "wikipedia_search",
+ "description": """A wide range of how-to and information pages are made available in wikipedia. Since 2001, it has grown rapidly to become the world's largest reference website. From Wikipedia, the free encyclopedia.""",
+ "parameters": {
+ "query": {
+ "type": "string",
+ "description": "The search keyword to execute with wikipedia. The keyword MUST be a specific subject that can match the title.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.top_n = 10
+ self.language = "en"
+
+ def check(self):
+ self.check_positive_integer(self.top_n, "Top N")
+ self.check_valid_value(self.language, "Wikipedia languages",
+ ['af', 'pl', 'ar', 'ast', 'az', 'bg', 'nan', 'bn', 'be', 'ca', 'cs', 'cy', 'da', 'de',
+ 'et', 'el', 'en', 'es', 'eo', 'eu', 'fa', 'fr', 'gl', 'ko', 'hy', 'hi', 'hr', 'id',
+ 'it', 'he', 'ka', 'lld', 'la', 'lv', 'lt', 'hu', 'mk', 'arz', 'ms', 'min', 'my', 'nl',
+ 'ja', 'nb', 'nn', 'ce', 'uz', 'pt', 'kk', 'ro', 'ru', 'ceb', 'sk', 'sl', 'sr', 'sh',
+ 'fi', 'sv', 'ta', 'tt', 'th', 'tg', 'azb', 'tr', 'uk', 'ur', 'vi', 'war', 'zh', 'yue'])
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "query": {
+ "name": "Query",
+ "type": "line"
+ }
+ }
+
+class Wikipedia(ToolBase, ABC):
+ component_name = "Wikipedia"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("query"):
+ self.set_output("formalized_content", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ try:
+ wikipedia.set_lang(self._param.language)
+ wiki_engine = wikipedia
+ pages = []
+ for p in wiki_engine.search(kwargs["query"], results=self._param.top_n):
+ try:
+ pages.append(wikipedia.page(p))
+ except Exception:
+ pass
+ self._retrieve_chunks(pages,
+ get_title=lambda r: r.title,
+ get_url=lambda r: r.url,
+ get_content=lambda r: r.summary)
+ return self.output("formalized_content")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"Wikipedia error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"Wikipedia error: {last_e}"
+
+ assert False, self.output()
diff --git a/agent/tools/yahoofinance.py b/agent/tools/yahoofinance.py
new file mode 100644
index 000000000..2b70f9616
--- /dev/null
+++ b/agent/tools/yahoofinance.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import time
+from abc import ABC
+import pandas as pd
+import yfinance as yf
+from agent.tools.base import ToolMeta, ToolParamBase, ToolBase
+from api.utils.api_utils import timeout
+
+
+class YahooFinanceParam(ToolParamBase):
+ """
+ Define the YahooFinance component parameters.
+ """
+
+ def __init__(self):
+ self.meta:ToolMeta = {
+ "name": "yahoo_finance",
+ "description": "The Yahoo Finance is a service that provides access to real-time and historical stock market data. It enables users to fetch various types of stock information, such as price quotes, historical prices, company profiles, and financial news. The API offers structured data, allowing developers to integrate market data into their applications and analysis tools.",
+ "parameters": {
+ "stock_code": {
+ "type": "string",
+ "description": "The stock code or company name.",
+ "default": "{sys.query}",
+ "required": True
+ }
+ }
+ }
+ super().__init__()
+ self.info = True
+ self.history = False
+ self.count = False
+ self.financials = False
+ self.income_stmt = False
+ self.balance_sheet = False
+ self.cash_flow_statement = False
+ self.news = True
+
+ def check(self):
+ self.check_boolean(self.info, "get all stock info")
+ self.check_boolean(self.history, "get historical market data")
+ self.check_boolean(self.count, "show share count")
+ self.check_boolean(self.financials, "show financials")
+ self.check_boolean(self.income_stmt, "income statement")
+ self.check_boolean(self.balance_sheet, "balance sheet")
+ self.check_boolean(self.cash_flow_statement, "cash flow statement")
+ self.check_boolean(self.news, "show news")
+
+ def get_input_form(self) -> dict[str, dict]:
+ return {
+ "stock_code": {
+ "name": "Stock code/Company name",
+ "type": "line"
+ }
+ }
+
+class YahooFinance(ToolBase, ABC):
+ component_name = "YahooFinance"
+
+ @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))
+ def _invoke(self, **kwargs):
+ if not kwargs.get("stock_code"):
+ self.set_output("report", "")
+ return ""
+
+ last_e = ""
+ for _ in range(self._param.max_retries+1):
+ yohoo_res = []
+ try:
+ msft = yf.Ticker(kwargs["stock_code"])
+ if self._param.info:
+ yohoo_res.append("# Information:\n" + pd.Series(msft.info).to_markdown() + "\n")
+ if self._param.history:
+ yohoo_res.append("# History:\n" + msft.history().to_markdown() + "\n")
+ if self._param.financials:
+ yohoo_res.append("# Calendar:\n" + pd.DataFrame(msft.calendar).to_markdown() + "\n")
+ if self._param.balance_sheet:
+ yohoo_res.append("# Balance sheet:\n" + msft.balance_sheet.to_markdown() + "\n")
+ yohoo_res.append("# Quarterly balance sheet:\n" + msft.quarterly_balance_sheet.to_markdown() + "\n")
+ if self._param.cash_flow_statement:
+ yohoo_res.append("# Cash flow statement:\n" + msft.cashflow.to_markdown() + "\n")
+ yohoo_res.append("# Quarterly cash flow statement:\n" + msft.quarterly_cashflow.to_markdown() + "\n")
+ if self._param.news:
+ yohoo_res.append("# News:\n" + pd.DataFrame(msft.news).to_markdown() + "\n")
+ self.set_output("report", "\n\n".join(yohoo_res))
+ return self.output("report")
+ except Exception as e:
+ last_e = e
+ logging.exception(f"YahooFinance error: {e}")
+ time.sleep(self._param.delay_after_error)
+
+ if last_e:
+ self.set_output("_ERROR", str(last_e))
+ return f"YahooFinance error: {last_e}"
+
+ assert False, self.output()
diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py
index b500d19f1..26ea74888 100644
--- a/api/apps/canvas_app.py
+++ b/api/apps/canvas_app.py
@@ -14,20 +14,35 @@
# limitations under the License.
#
import json
-import traceback
+import logging
+import re
+import sys
+from functools import partial
+
+import trio
from flask import request, Response
from flask_login import login_required, current_user
-from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService
+
+from agent.component import LLM
+from api.db import FileType
+from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService, API4ConversationService
+from api.db.services.document_service import DocumentService
+from api.db.services.file_service import FileService
from api.db.services.user_service import TenantService
from api.db.services.user_canvas_version import UserCanvasVersionService
from api.settings import RetCode
from api.utils import get_uuid
-from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result
+from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result, \
+ get_error_data_result
from agent.canvas import Canvas
from peewee import MySQLDatabase, PostgresqlDatabase
from api.db.db_models import APIToken
import time
+from api.utils.file_utils import filename_type, read_potential_broken_pdf
+from rag.utils.redis_conn import REDIS_CONN
+
+
@manager.route('/templates', methods=['GET']) # noqa: F821
@login_required
def templates():
@@ -112,8 +127,10 @@ def getsse(canvas_id):
@login_required
def run():
req = request.json
- stream = req.get("stream", True)
- running_hint_text = req.get("running_hint_text", "")
+ query = req.get("query", "")
+ files = req.get("files", [])
+ inputs = req.get("inputs", {})
+ user_id = req.get("user_id", current_user.id)
e, cvs = UserCanvasService.get_by_id(req["id"])
if not e:
return get_data_error_result(message="canvas not found.")
@@ -125,68 +142,29 @@ def run():
if not isinstance(cvs.dsl, str):
cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
- final_ans = {"reference": [], "content": ""}
- message_id = req.get("message_id", get_uuid())
try:
- canvas = Canvas(cvs.dsl, current_user.id)
- if "message" in req:
- canvas.messages.append({"role": "user", "content": req["message"], "id": message_id})
- canvas.add_user_input(req["message"])
+ canvas = Canvas(cvs.dsl, current_user.id, req["id"])
except Exception as e:
return server_error_response(e)
- if stream:
- def sse():
- nonlocal answer, cvs
- try:
- for ans in canvas.run(running_hint_text = running_hint_text, stream=True):
- if ans.get("running_status"):
- yield "data:" + json.dumps({"code": 0, "message": "",
- "data": {"answer": ans["content"],
- "running_status": True}},
- ensure_ascii=False) + "\n\n"
- continue
- for k in ans.keys():
- final_ans[k] = ans[k]
- ans = {"answer": ans["content"], "reference": ans.get("reference", [])}
- yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
+ def sse():
+ nonlocal canvas, user_id
+ try:
+ for ans in canvas.run(query=query, files=files, user_id=user_id, inputs=inputs):
+ yield "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
- canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
- canvas.history.append(("assistant", final_ans["content"]))
- if not canvas.path[-1]:
- canvas.path.pop(-1)
- if final_ans.get("reference"):
- canvas.reference.append(final_ans["reference"])
- cvs.dsl = json.loads(str(canvas))
- UserCanvasService.update_by_id(req["id"], cvs.to_dict())
- except Exception as e:
- cvs.dsl = json.loads(str(canvas))
- if not canvas.path[-1]:
- canvas.path.pop(-1)
- UserCanvasService.update_by_id(req["id"], cvs.to_dict())
- traceback.print_exc()
- yield "data:" + json.dumps({"code": 500, "message": str(e),
- "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
- ensure_ascii=False) + "\n\n"
- yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
+ cvs.dsl = json.loads(str(canvas))
+ UserCanvasService.update_by_id(req["id"], cvs.to_dict())
+ except Exception as e:
+ logging.exception(e)
+ yield "data:" + json.dumps({"code": 500, "message": str(e), "data": False}, ensure_ascii=False) + "\n\n"
- resp = Response(sse(), mimetype="text/event-stream")
- resp.headers.add_header("Cache-control", "no-cache")
- resp.headers.add_header("Connection", "keep-alive")
- resp.headers.add_header("X-Accel-Buffering", "no")
- resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
- return resp
-
- for answer in canvas.run(running_hint_text = running_hint_text, stream=False):
- if answer.get("running_status"):
- continue
- final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else ""
- canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
- if final_ans.get("reference"):
- canvas.reference.append(final_ans["reference"])
- cvs.dsl = json.loads(str(canvas))
- UserCanvasService.update_by_id(req["id"], cvs.to_dict())
- return get_json_result(data={"answer": final_ans["content"], "reference": final_ans.get("reference", [])})
+ resp = Response(sse(), mimetype="text/event-stream")
+ resp.headers.add_header("Cache-control", "no-cache")
+ resp.headers.add_header("Connection", "keep-alive")
+ resp.headers.add_header("X-Accel-Buffering", "no")
+ resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
+ return resp
@manager.route('/reset', methods=['POST']) # noqa: F821
@@ -212,9 +190,84 @@ def reset():
return server_error_response(e)
-@manager.route('/input_elements', methods=['GET']) # noqa: F821
+@manager.route("/upload/", methods=["POST"]) # noqa: F821
+def upload(canvas_id):
+ e, cvs = UserCanvasService.get_by_tenant_id(canvas_id)
+ if not e:
+ return get_data_error_result(message="canvas not found.")
+
+ user_id = cvs["user_id"]
+ def structured(filename, filetype, blob, content_type):
+ nonlocal user_id
+ if filetype == FileType.PDF.value:
+ blob = read_potential_broken_pdf(blob)
+
+ location = get_uuid()
+ FileService.put_blob(user_id, location, blob)
+
+ return {
+ "id": location,
+ "name": filename,
+ "size": sys.getsizeof(blob),
+ "extension": filename.split(".")[-1].lower(),
+ "mime_type": content_type,
+ "created_by": user_id,
+ "created_at": time.time(),
+ "preview_url": None
+ }
+
+ if request.args.get("url"):
+ from crawl4ai import (
+ AsyncWebCrawler,
+ BrowserConfig,
+ CrawlerRunConfig,
+ DefaultMarkdownGenerator,
+ PruningContentFilter,
+ CrawlResult
+ )
+ try:
+ url = request.args.get("url")
+ filename = re.sub(r"\?.*", "", url.split("/")[-1])
+ async def adownload():
+ browser_config = BrowserConfig(
+ headless=True,
+ verbose=False,
+ )
+ async with AsyncWebCrawler(config=browser_config) as crawler:
+ crawler_config = CrawlerRunConfig(
+ markdown_generator=DefaultMarkdownGenerator(
+ content_filter=PruningContentFilter()
+ ),
+ pdf=True,
+ screenshot=False
+ )
+ result: CrawlResult = await crawler.arun(
+ url=url,
+ config=crawler_config
+ )
+ return result
+ page = trio.run(adownload())
+ if page.pdf:
+ if filename.split(".")[-1].lower() != "pdf":
+ filename += ".pdf"
+ return get_json_result(data=structured(filename, "pdf", page.pdf, page.response_headers["content-type"]))
+
+ return get_json_result(data=structured(filename, "html", str(page.markdown).encode("utf-8"), page.response_headers["content-type"], user_id))
+
+ except Exception as e:
+ return server_error_response(e)
+
+ file = request.files['file']
+ try:
+ DocumentService.check_doc_health(user_id, file.filename)
+ return get_json_result(data=structured(file.filename, filename_type(file.filename), file.read(), file.content_type))
+ except Exception as e:
+ return server_error_response(e)
+
+
+@manager.route('/input_form', methods=['GET']) # noqa: F821
@login_required
-def input_elements():
+def input_form():
cvs_id = request.args.get("id")
cpn_id = request.args.get("component_id")
try:
@@ -227,7 +280,7 @@ def input_elements():
code=RetCode.OPERATING_ERROR)
canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
- return get_json_result(data=canvas.get_component_input_elements(cpn_id))
+ return get_json_result(data=canvas.get_component_input_form(cpn_id))
except Exception as e:
return server_error_response(e)
@@ -237,8 +290,6 @@ def input_elements():
@login_required
def debug():
req = request.json
- for p in req["params"]:
- assert p.get("key")
try:
e, user_canvas = UserCanvasService.get_by_id(req["id"])
if not e:
@@ -249,11 +300,22 @@ def debug():
code=RetCode.OPERATING_ERROR)
canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
- componant = canvas.get_component(req["component_id"])["obj"]
- componant.reset()
- componant._param.debug_inputs = req["params"]
- df = canvas.get_component(req["component_id"])["obj"].debug()
- return get_json_result(data=df.to_dict(orient="records"))
+ canvas.reset()
+ canvas.message_id = get_uuid()
+ component = canvas.get_component(req["component_id"])["obj"]
+ component.reset()
+
+ if isinstance(component, LLM):
+ component.set_debug_inputs(req["params"])
+ component.invoke(**{k: o["value"] for k,o in req["params"].items()})
+ outputs = component.output()
+ for k in outputs.keys():
+ if isinstance(outputs[k], partial):
+ txt = ""
+ for c in outputs[k]():
+ txt += c
+ outputs[k] = txt
+ return get_json_result(data=outputs)
except Exception as e:
return server_error_response(e)
@@ -292,6 +354,8 @@ def test_db_connect():
return get_json_result(data="Database Connection Successful!")
except Exception as e:
return server_error_response(e)
+
+
#api get list version dsl of canvas
@manager.route('/getlistversion/', methods=['GET']) # noqa: F821
@login_required
@@ -301,6 +365,8 @@ def getlistversion(canvas_id):
return get_json_result(data=list)
except Exception as e:
return get_data_error_result(message=f"Error getting history files: {e}")
+
+
#api get version dsl of canvas
@manager.route('/getversion/', methods=['GET']) # noqa: F821
@login_required
@@ -312,6 +378,8 @@ def getversion( version_id):
return get_json_result(data=version.to_dict())
except Exception as e:
return get_json_result(data=f"Error getting history file: {e}")
+
+
@manager.route('/listteam', methods=['GET']) # noqa: F821
@login_required
def list_kbs():
@@ -328,6 +396,8 @@ def list_kbs():
return get_json_result(data={"kbs": kbs, "total": total})
except Exception as e:
return server_error_response(e)
+
+
@manager.route('/setting', methods=['POST']) # noqa: F821
@validate_request("id", "title", "permission")
@login_required
@@ -351,3 +421,46 @@ def setting():
code=RetCode.OPERATING_ERROR)
num= UserCanvasService.update_by_id(req["id"], flow)
return get_json_result(data=num)
+
+
+@manager.route('/trace', methods=['GET']) # noqa: F821
+def trace():
+ cvs_id = request.args.get("canvas_id")
+ msg_id = request.args.get("message_id")
+ try:
+ bin = REDIS_CONN.get(f"{cvs_id}-{msg_id}-logs")
+ if not bin:
+ return get_json_result(data={})
+
+ return get_json_result(data=json.loads(bin.encode("utf-8")))
+ except Exception as e:
+ logging.exception(e)
+
+
+@manager.route('//sessions', methods=['GET']) # noqa: F821
+@login_required
+def sessions(canvas_id):
+ tenant_id = current_user.id
+ if not UserCanvasService.query(user_id=tenant_id, id=canvas_id):
+ return get_error_data_result(message=f"You don't own the agent {canvas_id}.")
+
+ user_id = request.args.get("user_id")
+ page_number = int(request.args.get("page", 1))
+ items_per_page = int(request.args.get("page_size", 30))
+ keywords = request.args.get("keywords")
+ from_date = request.args.get("from_date")
+ to_date = request.args.get("to_date")
+ orderby = request.args.get("orderby", "update_time")
+ if request.args.get("desc") == "False" or request.args.get("desc") == "false":
+ desc = False
+ else:
+ desc = True
+ # dsl defaults to True in all cases except for False and false
+ include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false"
+ total, sess = API4ConversationService.get_list(canvas_id, tenant_id, page_number, items_per_page, orderby, desc,
+ None, user_id, include_dsl, keywords, from_date, to_date)
+ try:
+ return get_json_result(data={"total": total, "sessions": sess})
+ except Exception as e:
+ return server_error_response(e)
+
diff --git a/api/apps/conversation_app.py b/api/apps/conversation_app.py
index 16d67bc57..3799d01bc 100644
--- a/api/apps/conversation_app.py
+++ b/api/apps/conversation_app.py
@@ -33,6 +33,7 @@ from api.db.services.user_service import UserTenantService
from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request
from graphrag.general.mind_map_extractor import MindMapExtractor
from rag.app.tag import label_question
+from rag.prompts.prompts import chunks_format
@manager.route("/set", methods=["POST"]) # noqa: F821
@@ -90,25 +91,10 @@ def get():
else:
return get_json_result(data=False, message="Only owner of conversation authorized for this operation.", code=settings.RetCode.OPERATING_ERROR)
- def get_value(d, k1, k2):
- return d.get(k1, d.get(k2))
-
for ref in conv.reference:
if isinstance(ref, list):
continue
- ref["chunks"] = [
- {
- "id": get_value(ck, "chunk_id", "id"),
- "content": get_value(ck, "content", "content_with_weight"),
- "document_id": get_value(ck, "doc_id", "document_id"),
- "document_name": get_value(ck, "docnm_kwd", "document_name"),
- "dataset_id": get_value(ck, "kb_id", "dataset_id"),
- "image_id": get_value(ck, "image_id", "img_id"),
- "positions": get_value(ck, "positions", "position_int"),
- "doc_type": get_value(ck, "doc_type", "doc_type_kwd"),
- }
- for ck in ref.get("chunks", [])
- ]
+ ref["chunks"] = chunks_format(ref)
conv = conv.to_dict()
conv["avatar"] = avatar
@@ -201,26 +187,10 @@ def completion():
if not conv.reference:
conv.reference = []
else:
-
- def get_value(d, k1, k2):
- return d.get(k1, d.get(k2))
-
for ref in conv.reference:
if isinstance(ref, list):
continue
- ref["chunks"] = [
- {
- "id": get_value(ck, "chunk_id", "id"),
- "content": get_value(ck, "content", "content_with_weight"),
- "document_id": get_value(ck, "doc_id", "document_id"),
- "document_name": get_value(ck, "docnm_kwd", "document_name"),
- "dataset_id": get_value(ck, "kb_id", "dataset_id"),
- "image_id": get_value(ck, "image_id", "img_id"),
- "positions": get_value(ck, "positions", "position_int"),
- "doc_type": get_value(ck, "doc_type_kwd", "doc_type_kwd"),
- }
- for ck in ref.get("chunks", [])
- ]
+ ref["chunks"] = chunks_format(ref)
if not conv.reference:
conv.reference = []
diff --git a/api/apps/dialog_app.py b/api/apps/dialog_app.py
index 42f496c72..add9f595e 100644
--- a/api/apps/dialog_app.py
+++ b/api/apps/dialog_app.py
@@ -51,7 +51,7 @@ def set_dialog():
vector_similarity_weight = req.get("vector_similarity_weight", 0.3)
llm_setting = req.get("llm_setting", {})
prompt_config = req["prompt_config"]
-
+
if not req.get("kb_ids", []) and not prompt_config.get("tavily_api_key") and "{knowledge}" in prompt_config['system']:
return get_data_error_result(message="Please remove `{knowledge}` in system prompt since no knowledge base/Tavily used here.")
diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py
index dd2722552..06e86bb86 100644
--- a/api/apps/sdk/session.py
+++ b/api/apps/sdk/session.py
@@ -556,7 +556,7 @@ def list_agent_session(tenant_id, agent_id):
desc = True
# dsl defaults to True in all cases except for False and false
include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false"
- convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, user_id, include_dsl)
+ total, convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, user_id, include_dsl)
if not convs:
return get_result(data=[])
for conv in convs:
@@ -817,9 +817,6 @@ def agent_bot_completions(agent_id):
if not objs:
return get_error_data_result(message='Authentication error: API key is invalid!"')
- if "quote" not in req:
- req["quote"] = False
-
if req.get("stream", True):
resp = Response(agent_completion(objs[0].tenant_id, agent_id, **req), mimetype="text/event-stream")
resp.headers.add_header("Cache-control", "no-cache")
@@ -830,3 +827,23 @@ def agent_bot_completions(agent_id):
for answer in agent_completion(objs[0].tenant_id, agent_id, **req):
return get_result(data=answer)
+
+
+@manager.route("/agentbots//inputs", methods=["GET"]) # noqa: F821
+def begin_inputs(agent_id):
+ token = request.headers.get("Authorization").split()
+ if len(token) != 2:
+ return get_error_data_result(message='Authorization is not valid!"')
+ token = token[1]
+ objs = APIToken.query(beta=token)
+ if not objs:
+ return get_error_data_result(message='Authentication error: API key is invalid!"')
+
+ e, cvs = UserCanvasService.get_by_id(agent_id)
+ if not e:
+ return get_error_data_result(f"Can't find agent by ID: {agent_id}")
+
+ canvas = Canvas(json.dumps(cvs.dsl), objs[0].tenant_id)
+ return get_result(data=canvas.get_component_input_form("begin"))
+
+
diff --git a/api/db/db_models.py b/api/db/db_models.py
index 1acf6bbca..038fc8406 100644
--- a/api/db/db_models.py
+++ b/api/db/db_models.py
@@ -463,6 +463,7 @@ class DataBaseModel(BaseModel):
@DB.connection_context()
+@DB.lock("init_database_tables", 60)
def init_database_tables(alter_fields=[]):
members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
table_objs = []
@@ -474,7 +475,7 @@ def init_database_tables(alter_fields=[]):
if not obj.table_exists():
logging.debug(f"start create table {obj.__name__}")
try:
- obj.create_table()
+ obj.create_table(safe=True)
logging.debug(f"create table success: {obj.__name__}")
except Exception as e:
logging.exception(e)
@@ -798,6 +799,7 @@ class API4Conversation(DataBaseModel):
duration = FloatField(default=0, index=True)
round = IntegerField(default=0, index=True)
thumb_up = IntegerField(default=0, index=True)
+ errors = TextField(null=True, help_text="errors")
class Meta:
db_table = "api_4_conversation"
@@ -1009,4 +1011,8 @@ def migrate_db():
migrate(migrator.add_column("document", "suffix", CharField(max_length=32, null=False, default="", help_text="The real file extension suffix", index=True)))
except Exception:
pass
- logging.disable(logging.NOTSET)
+ try:
+ migrate(migrator.add_column("api_4_conversation", "errors", TextField(null=True, help_text="errors")))
+ except Exception:
+ pass
+ logging.disable(logging.NOTSET)
\ No newline at end of file
diff --git a/api/db/init_data.py b/api/db/init_data.py
index b46b27ce6..390bce48e 100644
--- a/api/db/init_data.py
+++ b/api/db/init_data.py
@@ -154,6 +154,11 @@ def init_llm_factory():
def add_graph_templates():
dir = os.path.join(get_project_base_directory(), "agent", "templates")
+ CanvasTemplateService.filter_delete([1 == 1])
+ if not os.path.exists(dir):
+ logging.warning("Missing agent templates!")
+ return
+
for fnm in os.listdir(dir):
try:
cnvs = json.load(open(os.path.join(dir, fnm), "r",encoding="utf-8"))
@@ -162,7 +167,7 @@ def add_graph_templates():
except Exception:
CanvasTemplateService.update_by_id(cnvs["id"], cnvs)
except Exception:
- logging.exception("Add graph templates error: ")
+ logging.exception("Add agent templates error: ")
def init_web_data():
diff --git a/api/db/services/api_service.py b/api/db/services/api_service.py
index b393812e9..2fcbe0329 100644
--- a/api/db/services/api_service.py
+++ b/api/db/services/api_service.py
@@ -43,7 +43,9 @@ class API4ConversationService(CommonService):
@DB.connection_context()
def get_list(cls, dialog_id, tenant_id,
page_number, items_per_page,
- orderby, desc, id, user_id=None, include_dsl=True):
+ orderby, desc, id, user_id=None, include_dsl=True, keywords="",
+ from_date=None, to_date=None
+ ):
if include_dsl:
sessions = cls.model.select().where(cls.model.dialog_id == dialog_id)
else:
@@ -53,13 +55,20 @@ class API4ConversationService(CommonService):
sessions = sessions.where(cls.model.id == id)
if user_id:
sessions = sessions.where(cls.model.user_id == user_id)
+ if keywords:
+ sessions = sessions.where(peewee.fn.LOWER(cls.model.message).contains(keywords.lower()))
+ if from_date:
+ sessions = sessions.where(cls.model.create_date >= from_date)
+ if to_date:
+ sessions = sessions.where(cls.model.create_date <= to_date)
if desc:
sessions = sessions.order_by(cls.model.getter_by(orderby).desc())
else:
sessions = sessions.order_by(cls.model.getter_by(orderby).asc())
+ count = sessions.count()
sessions = sessions.paginate(page_number, items_per_page)
- return list(sessions.dicts())
+ return count, list(sessions.dicts())
@classmethod
@DB.connection_context()
diff --git a/api/db/services/canvas_service.py b/api/db/services/canvas_service.py
index 8bcb7b1bc..832ffb4f6 100644
--- a/api/db/services/canvas_service.py
+++ b/api/db/services/canvas_service.py
@@ -14,6 +14,7 @@
# limitations under the License.
#
import json
+import logging
import time
import traceback
from uuid import uuid4
@@ -22,11 +23,12 @@ from api.db import TenantPermission
from api.db.db_models import DB, CanvasTemplate, User, UserCanvas, API4Conversation
from api.db.services.api_service import API4ConversationService
from api.db.services.common_service import CommonService
-from api.db.services.conversation_service import structure_answer
from api.utils import get_uuid
from api.utils.api_utils import get_data_openai
import tiktoken
from peewee import fn
+
+
class CanvasTemplateService(CommonService):
model = CanvasTemplate
@@ -79,7 +81,7 @@ class UserCanvasService(CommonService):
# obj = cls.model.query(id=pid)[0]
return True, agents.dicts()[0]
except Exception as e:
- print(e)
+ logging.exception(e)
return False, None
@classmethod
@@ -119,120 +121,58 @@ class UserCanvasService(CommonService):
count = agents.count()
agents = agents.paginate(page_number, items_per_page)
return list(agents.dicts()), count
-
-def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
- e, cvs = UserCanvasService.get_by_id(agent_id)
- assert e, "Agent not found."
- assert cvs.user_id == tenant_id, "You do not own the agent."
- if not isinstance(cvs.dsl,str):
- cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
- canvas = Canvas(cvs.dsl, tenant_id)
- canvas.reset()
- message_id = str(uuid4())
- if not session_id:
- query = canvas.get_preset_param()
- if query:
- for ele in query:
- if not ele["optional"]:
- if not kwargs.get(ele["key"]):
- assert False, f"`{ele['key']}` is required"
- ele["value"] = kwargs[ele["key"]]
- if ele["optional"]:
- if kwargs.get(ele["key"]):
- ele["value"] = kwargs[ele['key']]
- else:
- if "value" in ele:
- ele.pop("value")
- cvs.dsl = json.loads(str(canvas))
+
+def completion(tenant_id, agent_id, session_id=None, **kwargs):
+ query = kwargs.get("query", "")
+ files = kwargs.get("files", [])
+ inputs = kwargs.get("inputs", {})
+ user_id = kwargs.get("user_id", "")
+
+ if session_id:
+ e, conv = API4ConversationService.get_by_id(session_id)
+ assert e, "Session not found!"
+ if not conv.message:
+ conv.message = []
+ canvas = Canvas(json.dumps(conv.dsl), tenant_id, session_id)
+ else:
+ e, cvs = UserCanvasService.get_by_id(agent_id)
+ assert e, "Agent not found."
+ assert cvs.user_id == tenant_id, "You do not own the agent."
+ if not isinstance(cvs.dsl, str):
+ cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
session_id=get_uuid()
+ canvas = Canvas(cvs.dsl, tenant_id, session_id)
conv = {
"id": session_id,
"dialog_id": cvs.id,
- "user_id": kwargs.get("user_id", "") if isinstance(kwargs, dict) else "",
- "message": [{"role": "assistant", "content": canvas.get_prologue(), "created_at": time.time()}],
+ "user_id": user_id,
+ "message": [],
"source": "agent",
"dsl": cvs.dsl
}
API4ConversationService.save(**conv)
conv = API4Conversation(**conv)
- else:
- e, conv = API4ConversationService.get_by_id(session_id)
- assert e, "Session not found!"
- canvas = Canvas(json.dumps(conv.dsl), tenant_id)
- canvas.messages.append({"role": "user", "content": question, "id": message_id})
- canvas.add_user_input(question)
- if not conv.message:
- conv.message = []
- conv.message.append({
- "role": "user",
- "content": question,
- "id": message_id
- })
- if not conv.reference:
- conv.reference = []
- conv.reference.append({"chunks": [], "doc_aggs": []})
- kwargs_changed = False
- if kwargs:
- query = canvas.get_preset_param()
- if query:
- for ele in query:
- if ele["key"] in kwargs:
- if ele["value"] != kwargs[ele["key"]]:
- ele["value"] = kwargs[ele["key"]]
- kwargs_changed = True
- if kwargs_changed:
- conv.dsl = json.loads(str(canvas))
- API4ConversationService.update_by_id(session_id, {"dsl": conv.dsl})
+ message_id = str(uuid4())
+ conv.message.append({
+ "role": "user",
+ "content": query,
+ "id": message_id
+ })
+ txt = ""
+ for ans in canvas.run(query=query, files=files, user_id=user_id, inputs=inputs):
+ ans["session_id"] = session_id
+ if ans["event"] == "message":
+ txt += ans["data"]["content"]
+ yield "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
- final_ans = {"reference": [], "content": ""}
- if stream:
- try:
- for ans in canvas.run(stream=stream):
- if ans.get("running_status"):
- yield "data:" + json.dumps({"code": 0, "message": "",
- "data": {"answer": ans["content"],
- "running_status": True}},
- ensure_ascii=False) + "\n\n"
- continue
- for k in ans.keys():
- final_ans[k] = ans[k]
- ans = {"answer": ans["content"], "reference": ans.get("reference", []), "param": canvas.get_preset_param()}
- ans = structure_answer(conv, ans, message_id, session_id)
- yield "data:" + json.dumps({"code": 0, "message": "", "data": ans},
- ensure_ascii=False) + "\n\n"
+ conv.message.append({"role": "assistant", "content": txt, "created_at": time.time(), "id": message_id})
+ conv.reference = canvas.get_reference()
+ conv.errors = canvas.error
+ API4ConversationService.append_message(conv.id, conv.to_dict())
- canvas.messages.append({"role": "assistant", "content": final_ans["content"], "created_at": time.time(), "id": message_id})
- canvas.history.append(("assistant", final_ans["content"]))
- if final_ans.get("reference"):
- canvas.reference.append(final_ans["reference"])
- conv.dsl = json.loads(str(canvas))
- API4ConversationService.append_message(conv.id, conv.to_dict())
- except Exception as e:
- traceback.print_exc()
- conv.dsl = json.loads(str(canvas))
- API4ConversationService.append_message(conv.id, conv.to_dict())
- yield "data:" + json.dumps({"code": 500, "message": str(e),
- "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
- ensure_ascii=False) + "\n\n"
- yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
- else:
- for answer in canvas.run(stream=False):
- if answer.get("running_status"):
- continue
- final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else ""
- canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
- if final_ans.get("reference"):
- canvas.reference.append(final_ans["reference"])
- conv.dsl = json.loads(str(canvas))
-
- result = {"answer": final_ans["content"], "reference": final_ans.get("reference", []) , "param": canvas.get_preset_param()}
- result = structure_answer(conv, result, message_id, session_id)
- API4ConversationService.append_message(conv.id, conv.to_dict())
- yield result
- break
def completionOpenAI(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
"""Main function for OpenAI-compatible completions, structured similarly to the completion function."""
tiktokenenc = tiktoken.get_encoding("cl100k_base")
diff --git a/api/db/services/document_service.py b/api/db/services/document_service.py
index ec8e5f64a..4b60275d3 100644
--- a/api/db/services/document_service.py
+++ b/api/db/services/document_service.py
@@ -27,7 +27,7 @@ import xxhash
from peewee import fn
from api import settings
-from api.constants import IMG_BASE64_PREFIX
+from api.constants import IMG_BASE64_PREFIX, FILE_NAME_LEN_LIMIT
from api.db import FileType, LLMType, ParserType, StatusEnum, TaskStatus, UserTenantRole
from api.db.db_models import DB, Document, Knowledgebase, Task, Tenant, UserTenant, File2Document, File
from api.db.db_utils import bulk_insert_into_db
@@ -100,6 +100,17 @@ class DocumentService(CommonService):
docs = docs.paginate(page_number, items_per_page)
return list(docs.dicts()), count
+ @classmethod
+ @DB.connection_context()
+ def check_doc_health(cls, tenant_id: str, filename):
+ import os
+ MAX_FILE_NUM_PER_USER = int(os.environ.get("MAX_FILE_NUM_PER_USER", 0))
+ if MAX_FILE_NUM_PER_USER > 0 and DocumentService.get_doc_count(tenant_id) >= MAX_FILE_NUM_PER_USER:
+ raise RuntimeError("Exceed the maximum file number of a free user!")
+ if len(filename.encode("utf-8")) > FILE_NAME_LEN_LIMIT:
+ raise RuntimeError("Exceed the maximum length of file name!")
+ return True
+
@classmethod
@DB.connection_context()
def get_by_kb_id(cls, kb_id, page_number, items_per_page,
@@ -258,13 +269,13 @@ class DocumentService(CommonService):
)
if len(graph_source) > 0 and doc.id in list(graph_source.values())[0]["source_id"]:
settings.docStoreConn.update({"kb_id": doc.kb_id, "knowledge_graph_kwd": ["entity", "relation", "graph", "subgraph", "community_report"], "source_id": doc.id},
- {"remove": {"source_id": doc.id}},
- search.index_name(tenant_id), doc.kb_id)
+ {"remove": {"source_id": doc.id}},
+ search.index_name(tenant_id), doc.kb_id)
settings.docStoreConn.update({"kb_id": doc.kb_id, "knowledge_graph_kwd": ["graph"]},
- {"removed_kwd": "Y"},
- search.index_name(tenant_id), doc.kb_id)
+ {"removed_kwd": "Y"},
+ search.index_name(tenant_id), doc.kb_id)
settings.docStoreConn.delete({"kb_id": doc.kb_id, "knowledge_graph_kwd": ["entity", "relation", "graph", "subgraph", "community_report"], "must_not": {"exists": "source_id"}},
- search.index_name(tenant_id), doc.kb_id)
+ search.index_name(tenant_id), doc.kb_id)
except Exception:
pass
return cls.delete_by_id(doc.id)
@@ -323,9 +334,9 @@ class DocumentService(CommonService):
"Document not found which is supposed to be there")
num = Knowledgebase.update(
token_num=Knowledgebase.token_num +
- token_num,
+ token_num,
chunk_num=Knowledgebase.chunk_num +
- chunk_num).where(
+ chunk_num).where(
Knowledgebase.id == kb_id).execute()
return num
@@ -341,9 +352,9 @@ class DocumentService(CommonService):
"Document not found which is supposed to be there")
num = Knowledgebase.update(
token_num=Knowledgebase.token_num -
- token_num,
+ token_num,
chunk_num=Knowledgebase.chunk_num -
- chunk_num
+ chunk_num
).where(
Knowledgebase.id == kb_id).execute()
return num
@@ -356,9 +367,9 @@ class DocumentService(CommonService):
num = Knowledgebase.update(
token_num=Knowledgebase.token_num -
- doc.token_num,
+ doc.token_num,
chunk_num=Knowledgebase.chunk_num -
- doc.chunk_num,
+ doc.chunk_num,
doc_num=Knowledgebase.doc_num - 1
).where(
Knowledgebase.id == doc.kb_id).execute()
@@ -388,7 +399,7 @@ class DocumentService(CommonService):
docs = cls.model.select(
Knowledgebase.tenant_id).join(
Knowledgebase, on=(
- Knowledgebase.id == cls.model.kb_id)).where(
+ Knowledgebase.id == cls.model.kb_id)).where(
cls.model.id == doc_id, Knowledgebase.status == StatusEnum.VALID.value)
docs = docs.dicts()
if not docs:
@@ -410,7 +421,7 @@ class DocumentService(CommonService):
docs = cls.model.select(
Knowledgebase.tenant_id).join(
Knowledgebase, on=(
- Knowledgebase.id == cls.model.kb_id)).where(
+ Knowledgebase.id == cls.model.kb_id)).where(
cls.model.name == name, Knowledgebase.status == StatusEnum.VALID.value)
docs = docs.dicts()
if not docs:
@@ -423,7 +434,7 @@ class DocumentService(CommonService):
docs = cls.model.select(
cls.model.id).join(
Knowledgebase, on=(
- Knowledgebase.id == cls.model.kb_id)
+ Knowledgebase.id == cls.model.kb_id)
).join(UserTenant, on=(UserTenant.tenant_id == Knowledgebase.tenant_id)
).where(cls.model.id == doc_id, UserTenant.user_id == user_id).paginate(0, 1)
docs = docs.dicts()
@@ -435,12 +446,12 @@ class DocumentService(CommonService):
@DB.connection_context()
def accessible4deletion(cls, doc_id, user_id):
docs = cls.model.select(cls.model.id
- ).join(
+ ).join(
Knowledgebase, on=(
- Knowledgebase.id == cls.model.kb_id)
+ Knowledgebase.id == cls.model.kb_id)
).join(
UserTenant, on=(
- (UserTenant.tenant_id == Knowledgebase.created_by) & (UserTenant.user_id == user_id))
+ (UserTenant.tenant_id == Knowledgebase.created_by) & (UserTenant.user_id == user_id))
).where(
cls.model.id == doc_id,
UserTenant.status == StatusEnum.VALID.value,
@@ -457,7 +468,7 @@ class DocumentService(CommonService):
docs = cls.model.select(
Knowledgebase.embd_id).join(
Knowledgebase, on=(
- Knowledgebase.id == cls.model.kb_id)).where(
+ Knowledgebase.id == cls.model.kb_id)).where(
cls.model.id == doc_id, Knowledgebase.status == StatusEnum.VALID.value)
docs = docs.dicts()
if not docs:
@@ -499,7 +510,7 @@ class DocumentService(CommonService):
if not doc_id:
return
return doc_id[0]["id"]
-
+
@classmethod
@DB.connection_context()
def get_doc_ids_by_doc_names(cls, doc_names):
@@ -612,7 +623,7 @@ class DocumentService(CommonService):
info = {
"process_duration": datetime.timestamp(
datetime.now()) -
- d["process_begin_at"].timestamp(),
+ d["process_begin_at"].timestamp(),
"run": status}
if prg != 0:
info["progress"] = prg
diff --git a/api/db/services/file_service.py b/api/db/services/file_service.py
index 34033771f..767b389e9 100644
--- a/api/db/services/file_service.py
+++ b/api/db/services/file_service.py
@@ -14,7 +14,6 @@
# limitations under the License.
#
import logging
-import os
import re
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
@@ -22,7 +21,6 @@ from pathlib import Path
from flask_login import current_user
from peewee import fn
-from api.constants import FILE_NAME_LEN_LIMIT
from api.db import KNOWLEDGEBASE_FOLDER_NAME, FileSource, FileType, ParserType
from api.db.db_models import DB, Document, File, File2Document, Knowledgebase
from api.db.services import duplicate_name
@@ -31,6 +29,7 @@ from api.db.services.document_service import DocumentService
from api.db.services.file2document_service import File2DocumentService
from api.utils import get_uuid
from api.utils.file_utils import filename_type, read_potential_broken_pdf, thumbnail_img
+from rag.llm.cv_model import GptV4
from rag.utils.storage_factory import STORAGE_IMPL
@@ -411,12 +410,7 @@ class FileService(CommonService):
err, files = [], []
for file in file_objs:
try:
- MAX_FILE_NUM_PER_USER = int(os.environ.get("MAX_FILE_NUM_PER_USER", 0))
- if MAX_FILE_NUM_PER_USER > 0 and DocumentService.get_doc_count(kb.tenant_id) >= MAX_FILE_NUM_PER_USER:
- raise RuntimeError("Exceed the maximum file number of a free user!")
- if len(file.filename.encode("utf-8")) > FILE_NAME_LEN_LIMIT:
- raise RuntimeError(f"File name must be {FILE_NAME_LEN_LIMIT} bytes or less.")
-
+ DocumentService.check_doc_health(kb.tenant_id, file.filename)
filename = duplicate_name(DocumentService.query, name=file.filename, kb_id=kb.id)
filetype = filename_type(filename)
if filetype == FileType.OTHER.value:
@@ -463,6 +457,19 @@ class FileService(CommonService):
@staticmethod
def parse_docs(file_objs, user_id):
+ exe = ThreadPoolExecutor(max_workers=12)
+ threads = []
+ for file in file_objs:
+ threads.append(exe.submit(FileService.parse, file.filename, file.read(), False))
+
+ res = []
+ for th in threads:
+ res.append(th.result())
+
+ return "\n\n".join(res)
+
+ @staticmethod
+ def parse(filename, blob, img_base64=True, tenant_id=None):
from rag.app import audio, email, naive, picture, presentation
def dummy(prog=None, msg=""):
@@ -470,19 +477,12 @@ class FileService(CommonService):
FACTORY = {ParserType.PRESENTATION.value: presentation, ParserType.PICTURE.value: picture, ParserType.AUDIO.value: audio, ParserType.EMAIL.value: email}
parser_config = {"chunk_token_num": 16096, "delimiter": "\n!?;。;!?", "layout_recognize": "Plain Text"}
- exe = ThreadPoolExecutor(max_workers=12)
- threads = []
- for file in file_objs:
- kwargs = {"lang": "English", "callback": dummy, "parser_config": parser_config, "from_page": 0, "to_page": 100000, "tenant_id": user_id}
- filetype = filename_type(file.filename)
- blob = file.read()
- threads.append(exe.submit(FACTORY.get(FileService.get_parser(filetype, file.filename, ""), naive).chunk, file.filename, blob, **kwargs))
-
- res = []
- for th in threads:
- res.append("\n".join([ck["content_with_weight"] for ck in th.result()]))
-
- return "\n\n".join(res)
+ kwargs = {"lang": "English", "callback": dummy, "parser_config": parser_config, "from_page": 0, "to_page": 100000, "tenant_id": current_user.id if current_user else tenant_id}
+ file_type = filename_type(filename)
+ if img_base64 and file_type == FileType.VISUAL.value:
+ return GptV4.image2base64(blob)
+ cks = FACTORY.get(FileService.get_parser(filename_type(filename), filename, ""), naive).chunk(filename, blob, **kwargs)
+ return "\n".join([ck["content_with_weight"] for ck in cks])
@staticmethod
def get_parser(doc_type, filename, default):
@@ -495,3 +495,14 @@ class FileService(CommonService):
if re.search(r"\.(eml)$", filename):
return ParserType.EMAIL.value
return default
+
+ @staticmethod
+ def get_blob(user_id, location):
+ bname = f"{user_id}-downloads"
+ return STORAGE_IMPL.get(bname, location)
+
+ @staticmethod
+ def put_blob(user_id, location, blob):
+ bname = f"{user_id}-downloads"
+ return STORAGE_IMPL.put(bname, location, blob)
+
diff --git a/api/db/services/llm_service.py b/api/db/services/llm_service.py
index 92655abfd..258a4bf92 100644
--- a/api/db/services/llm_service.py
+++ b/api/db/services/llm_service.py
@@ -14,6 +14,8 @@
# limitations under the License.
#
import logging
+import re
+from functools import partial
from langfuse import Langfuse
@@ -137,7 +139,7 @@ class TenantLLMService(CommonService):
@classmethod
@DB.connection_context()
- def model_instance(cls, tenant_id, llm_type, llm_name=None, lang="Chinese"):
+ def model_instance(cls, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
model_config = TenantLLMService.get_model_config(tenant_id, llm_type, llm_name)
if llm_type == LLMType.EMBEDDING.value:
if model_config["llm_factory"] not in EmbeddingModel:
@@ -152,12 +154,12 @@ class TenantLLMService(CommonService):
if llm_type == LLMType.IMAGE2TEXT.value:
if model_config["llm_factory"] not in CvModel:
return
- return CvModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], lang, base_url=model_config["api_base"])
+ return CvModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], lang, base_url=model_config["api_base"], **kwargs)
if llm_type == LLMType.CHAT.value:
if model_config["llm_factory"] not in ChatModel:
return
- return ChatModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], base_url=model_config["api_base"])
+ return ChatModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], base_url=model_config["api_base"], **kwargs)
if llm_type == LLMType.SPEECH2TEXT:
if model_config["llm_factory"] not in Seq2txtModel:
@@ -221,20 +223,21 @@ class TenantLLMService(CommonService):
for llm_factory in llm_factories:
for llm in llm_factory["llm"]:
if llm_id == llm["llm_name"]:
- return llm["model_type"].strip(",")[-1]
+ return llm["model_type"].split(",")[-1]
class LLMBundle:
- def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese"):
+ def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
self.tenant_id = tenant_id
self.llm_type = llm_type
self.llm_name = llm_name
- self.mdl = TenantLLMService.model_instance(tenant_id, llm_type, llm_name, lang=lang)
+ self.mdl = TenantLLMService.model_instance(tenant_id, llm_type, llm_name, lang=lang, **kwargs)
assert self.mdl, "Can't find model for {}/{}/{}".format(tenant_id, llm_type, llm_name)
model_config = TenantLLMService.get_model_config(tenant_id, llm_type, llm_name)
self.max_length = model_config.get("max_tokens", 8192)
self.is_tools = model_config.get("is_tools", False)
+ self.verbose_tool_use = kwargs.get("verbose_tool_use")
langfuse_keys = TenantLangfuseService.filter_by_tenant(tenant_id=tenant_id)
if langfuse_keys:
@@ -331,7 +334,7 @@ class LLMBundle:
return txt
- def tts(self, text):
+ def tts(self, text: str) -> None:
if self.langfuse:
span = self.trace.span(name="tts", input={"text": text})
@@ -359,17 +362,20 @@ class LLMBundle:
return txt[last_think_end + len("