import pydantic
from pydantic import BaseModel
from typing import Optional

class ChatMessageItemReq(BaseModel):
    role: str = pydantic.Field(..., description="角色")
    content: str = pydantic.Field(..., description="内容")
    images: Optional[str] = pydantic.Field(default=None, description="图片列表，Base64编码")

class ChatMessageReq(BaseModel):
    messages: list[ChatMessageItemReq] = pydantic.Field(..., description="消息列表")
UPLOAD_DIR = "./uploaded_images"


def save_base64_image(base64_str: str, upload_dir: str = UPLOAD_DIR) -> str:
    try:
        # 生成唯一文件名
        filename = f"{uuid.uuid4().hex}.png"
        file_path = os.path.join(upload_dir, filename)

        # 解码 Base64 并保存为文件
        with open(file_path, "wb") as f:
            f.write(base64.b64decode(base64_str))
        return file_path
    except Exception as e:
        raise RuntimeError(f"保存图片失败: {str(e)}")



@router.post("/chat_sse")
async def chat_sse(
        req: ChatMessageReq = Body(),
        user_id: int = require_perm(PermTypeEnum.PERM_SPAC_CREATE_SPAC.value.code)  # 接口权限定义
) -> StreamingResponse:
    messages = req
    if messages is None:
        return StreamingResponse("messages is None", status_code=400)
    
    # input为最后一个message内容
    input = messages.messages[-1].content
    logger_helper.get_server_logger().info(f"user_id={user_id} chat,input: {input} .")
    
    images_base64 = input.images
    if images_base64:
        image_path = save_base64_image(images_base64)
        logger_helper.get_server_logger().info(f"用户上传图片已保存: {image_path}")

    # 移除最后一个message
    messages = messages.messages[:-1]
    # 查询RAG
    rag_list = rag_service.search_similar_docs(input)
    rag_content = ""
    # 提取rag list的内容
    for item in rag_list:
        rag_content += item.page_content + "\n"
    logger_helper.get_server_logger().info(f"user_id={user_id} chat,rag content: {rag_content} .")
    input = "请根据以下内容生成回复：\n" + rag_content + "\n\n" + input
    # 调用chat_service.chat
    historyMessage = ChatMessageReq(messages=messages)
    return chat_service.chat(input, history=historyMessage)


这是我的后端支持每轮对话附带一张图片。

<template>
  <div class="app-container">
    <div class="chat">
      <div class="chat-messages" ref="messagesContainer">
        <div v-for="(message, index) in messages" :key="index"
             :class="['message', message.role === 'user' ? 'user-message' : 'assistant-message']">
          <el-text :type="message.role === 'user' ? 'primary' : 'info'">{{ message.content }}</el-text>
        </div>
        <div v-if="isLoading && !hasBotResponseStarted" class="message assistant-message">
          <el-text type="info">
            <el-skeleton :rows="1" animated />
          </el-text>
        </div>
      </div>
      <div class="chat-input">
        <el-input
          v-model="userInput"
          type="textarea"
          :rows="3"
          placeholder="输入消息..."
          @keydown.enter.prevent="sendMessage"
        />
        <el-button
          type="primary"
          @click="sendMessage"
          :loading="isLoading"
          :disabled="!userInput.trim()"
        >
          发送
        </el-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { streamChatMessage } from '@/api/sse.js'


const userInput = ref('');
const messages = ref([]);
const isLoading = ref(false);
const messagesContainer = ref(null);
const hasBotResponseStarted = ref(false)

// 滚动到最新消息
const scrollToBottom = async () => {
  await nextTick();
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
  }
};

// 监听消息变化，自动滚动到底部
watch(messages, () => {
  scrollToBottom();
});


// 发送消息到后端
const assistantMessage = ref(null)

const sendMessage = async () => {
  const input = userInput.value.trim()
  if (!input) return

  // 添加用户消息
  messages.value.push({ role: 'user', content: input })

  isLoading.value = true
  userInput.value = ''
  hasBotResponseStarted.value = false
  assistantMessage.value = null

  try {
    await streamChatMessage({
      message: messages.value,
      onMessage: (chunk) => {
        if (!assistantMessage.value) {
          // 创建响应式 assistant 消息对象
          assistantMessage.value = { role: 'assistant', content: chunk }
          messages.value.push(assistantMessage.value)
          hasBotResponseStarted.value = true
        } else {
          assistantMessage.value.content += chunk
        }
      }
    })
  } catch (error) {
    ElMessage.error('请求失败')
  } finally {
    isLoading.value = false
    setTimeout(() => {
      hasBotResponseStarted.value = false
    }, 100)
  }
}
onMounted(() => {
  scrollToBottom();
});

</script>

帮我修改一下前端代码支持每轮对话选择上传一张使用base64编码的图片。