/ src / renderer / screens / agent / AgentCentralStage.vue
AgentCentralStage.vue
  1  <script setup>
  2  import { useMcpStore, getServers } from '@/renderer/store/mcp'
  3  import { useAgentStore } from '@/renderer/store/agent'
  4  import { useI18n } from 'vue-i18n'
  5  import { v4 as uuidv4 } from 'uuid'
  6  import { ref, watch, onMounted, computed } from 'vue'
  7  const mcpStore = useMcpStore()
  8  const agentStore = useAgentStore()
  9  const { t } = useI18n()
 10  
 11  const selectedTree = computed({
 12    get() {
 13      return agentStore.getRevised?.selectedNode
 14    },
 15    set(value) {
 16      console.log('Selected tools', value)
 17      if (agentStore.getRevised) {
 18        agentStore.getRevised.selectedNode = value
 19      }
 20    }
 21  })
 22  
 23  const items = ref([
 24    {
 25      id: 1,
 26      name: computed(() => t('agent.all')),
 27      children: []
 28    }
 29  ])
 30  
 31  watch(
 32    () => agentStore.allTools,
 33    (val) => {
 34      if (!agentStore.hasTools) {
 35        return
 36      }
 37      console.log('Tools Updated', val)
 38      const flatChildren = []
 39      const children = val
 40        .filter((serverType) => serverType.tools && serverType.tools.length > 0)
 41        .map((serverType) => {
 42          const serverNode = {
 43            id: serverType.server,
 44            name: serverType.server,
 45            children: serverType.tools.map((tool) => {
 46              const toolId = agentStore.genId(serverType.server, tool.name)
 47              const toolNode = {
 48                id: toolId,
 49                server: serverType.server,
 50                name: tool.name
 51              }
 52              flatChildren.push(toolId)
 53              return toolNode
 54            })
 55          }
 56          return serverNode
 57        })
 58      const rootObj = items.value[0]
 59      rootObj.children = children
 60      items.value = [rootObj]
 61  
 62      agentStore.agents.forEach((agent) => {
 63        const newSelectedNode = agent.selectedNode.filter((node) => {
 64          return flatChildren.includes(node)
 65        })
 66        agent.selectedNode = newSelectedNode
 67      })
 68    }
 69  )
 70  
 71  watch(
 72    () => mcpStore.version,
 73    (_val) => {
 74      load()
 75    }
 76  )
 77  
 78  onMounted(() => {
 79    load()
 80  })
 81  
 82  function load() {
 83    const mcpServers = getServers()
 84    const mcpKeys = Object.keys(mcpServers)
 85    // Create an array of Promises
 86    const toolPromises = mcpKeys.map((key) => {
 87      const toolsListFunction = mcpServers[key]?.tools?.list
 88      if (typeof toolsListFunction === 'function') {
 89        // Ensure that toolsListFunction() returns a Promise
 90        return Promise.resolve(toolsListFunction()).then((tools) => ({
 91          server: key,
 92          ...tools
 93        }))
 94      } else {
 95        // If toolsListFunction is not a function, return an object with content as null
 96        return Promise.resolve({
 97          name: key,
 98          tools: []
 99        })
100      }
101    })
102  
103    console.log(toolPromises)
104  
105    // Return a Promise that resolves when all toolPromises are resolved
106  
107    return Promise.all(toolPromises).then((data) => {
108      console.log(data)
109      agentStore.allTools = data
110    })
111  }
112  
113  function onClickClose(selection) {
114    selectedTree.value = selectedTree.value.filter((item) => item !== selection)
115  }
116  
117  function handleNameUpdate() {
118    if (!agentStore.getRevised.name) {
119      agentStore.getRevised.name = `Agent ${uuidv4()}`
120    }
121  
122    if (agentStore.getUnrevised.find((agent) => agent.name === agentStore.getRevised.name)) {
123      agentStore.getRevised.name = `Agent ${uuidv4()}`
124    }
125  }
126  </script>
127  
128  <template>
129    <!-- <v-btn @click="console.log(agentStore.allTools)"></v-btn> -->
130    <div v-if="agentStore.getRevised" :key="agentStore.getRevised">
131      <v-card :title="$t('agent.config')">
132        <v-divider></v-divider>
133        <v-text-field
134          v-model="agentStore.getRevised.name"
135          density="compact"
136          variant="outlined"
137          class="px-6 pt-5"
138          :label="$t('setting.name')"
139          @blur="handleNameUpdate"
140        ></v-text-field>
141      </v-card>
142  
143      <v-confirm-edit
144        v-model="agentStore.getRevised.prompt"
145        :cancel-text="$t('agent.cancel')"
146        :ok-text="$t('agent.save')"
147      >
148        <template #default="{ model: proxyModel, actions, isPristine }">
149          <v-card class="mt-4" :title="$t('agent.prompt')">
150            <v-divider></v-divider>
151            <template #text>
152              <v-textarea
153                v-model="proxyModel.value"
154                class="mx-2 mt-2"
155                auto-grow
156                variant="solo-filled"
157                :error-messages="isPristine ? '' : 'Please save'"
158              ></v-textarea>
159            </template>
160            <template #actions>
161              <v-spacer></v-spacer>
162              <component :is="actions" color="primary"></component>
163            </template>
164          </v-card>
165        </template>
166      </v-confirm-edit>
167  
168      <v-alert
169        v-if="!agentStore.hasTools"
170        border="top"
171        type="warning"
172        variant="outlined"
173        prominent
174        class="mt-4"
175      >
176        {{ $t('agent.no-tools') }}
177      </v-alert>
178      <v-card v-else class="mt-4" :title="$t('agent.tools')">
179        <v-row density="compact">
180          <v-divider></v-divider>
181          <v-treeview
182            v-model:selected="selectedTree"
183            :items="items"
184            :load="load"
185            class="flex-1-0"
186            false-icon="mdi-bookmark-outline"
187            indeterminate-icon="mdi-bookmark-minus"
188            item-title="name"
189            item-value="id"
190            select-strategy="classic"
191            true-icon="mdi-bookmark"
192            selectable
193          ></v-treeview>
194        </v-row>
195  
196        <v-row density="compact">
197          <v-divider></v-divider>
198          <v-card-text>
199            <div
200              v-if="selectedTree.length === 0"
201              class="text-h6 font-weight-light text-grey pa-4 text-center"
202            >
203              {{ $t('agent.selected') }}
204            </div>
205            <div class="d-flex flex-wrap ga-1">
206              <v-scroll-x-transition group hide-on-leave>
207                <div v-for="(selection, index) in selectedTree" :key="index">
208                  <v-chip
209                    v-if="selection"
210                    :key="selection"
211                    :text="agentStore.getId(selection).name"
212                    color="grey"
213                    size="small"
214                    border
215                    closable
216                    label
217                    @click="console.log(selection, index)"
218                    @click:close="onClickClose(selection)"
219                  >
220                    <template #prepend>
221                      <v-avatar
222                        :text="agentStore.getAbbr(agentStore.getId(selection).server)"
223                        :color="agentStore.getColor(selection)"
224                        start
225                        variant="plain"
226                      >
227                      </v-avatar>
228                    </template>
229                  </v-chip>
230                </div>
231              </v-scroll-x-transition>
232            </div>
233          </v-card-text>
234        </v-row>
235      </v-card>
236    </div>
237  </template>