当前位置:   article > 正文

Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装_unity gpt4o

unity gpt4o

Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装

目录

Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装

一、简单介绍

二、实现原理

三、注意实现

四、简单效果预览

五、案例简单实现步骤

六、关键代码


一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

本节介绍,这里在使用微软的Azure 进行语音合成的两个方法的做简单整理,这里简单说明,如果你有更好的方法,欢迎留言交流。

官网注册:

面向学生的 Azure - 免费帐户额度 | Microsoft Azure

官网技术文档网址:

技术文档 | Microsoft Learn

Azure OpenAI 一些模型介绍:

Azure OpenAI 服务模型 - Azure OpenAI | Microsoft Learn

GPT-4o 和 GPT-4 Turbo

GPT-4o 是 OpenAI 的最新模型。 GPT-4o 在单个模型中集成文本和图像,从而能够同时处理多个数据类型。 这种多模式方法提高了人机交互的准确性和响应能力。 GPT-4o 在英语文本和编码任务方面与 GPT-4 Turbo 相当,但在非英语语言和视觉任务方面具有更优越的性能,为 AI 功能设定了新的基准。

如何访问 GPT-4o 模型?

GPT-4o 可用于标准和全球标准模型部署。

需要在该模型可用的受支持标准全球标准区域中创建或使用现有资源。

创建资源后,可以部署 GPT-4o 模型。 

发起请求的json 格式:

  1. {
  2. "messages": [
  3. {
  4. "role": "system",
  5. "content": "You are a helpful assistant."
  6. },
  7. {
  8. "role": "user",
  9. "content": [
  10. {
  11. "type": "text",
  12. "text": "Describe this picture:"
  13. },
  14. {
  15. "type": "image_url",
  16. "image_url": {
  17. "url": "<image URL>"
  18. }
  19. }
  20. ]
  21. }
  22. ],
  23. "max_tokens": 100,
  24. "stream": false
  25. }

返回的格式根据是否是流式返回,略有所不同:

非流式的:

  1. {
  2. "id": "chatcmpl-8X0uY6Xbv4XpU4zo0KXhVk4iTldJf",
  3. "object": "chat.completion",
  4. "created": 1702879018,
  5. "model": "gpt-4v",
  6. "prompt_filter_results": [{
  7. "prompt_index": 0,
  8. "content_filter_results": {
  9. "hate": {
  10. "filtered": false,
  11. "severity": "safe"
  12. },
  13. "self_harm": {
  14. "filtered": false,
  15. "severity": "safe"
  16. },
  17. "sexual": {
  18. "filtered": false,
  19. "severity": "safe"
  20. },
  21. "violence": {
  22. "filtered": false,
  23. "severity": "safe"
  24. }
  25. }
  26. }],
  27. "choices": [{
  28. "finish_details": {
  29. "type": "stop",
  30. "stop": "<|fim_suffix|>"
  31. },
  32. "index": 0,
  33. "message": {
  34. "role": "assistant",
  35. "content": "这张图片展示了一辆银灰色的跑车,停靠在一个内饰精致的展厅里。车身呈流线型,前脸上有显著的品牌标志和数字“10”。车身上的漆面光洁明亮,反射着展厅内柔和的灯光。背景中可以看到其他几辆停放整齐的高端车辆,以及墙上挂着的一些装饰性画作。整个场景显得高端大气,彰显了跑车的豪华品质。"
  36. },
  37. "content_filter_results": {
  38. "hate": {
  39. "filtered": false,
  40. "severity": "safe"
  41. },
  42. "self_harm": {
  43. "filtered": false,
  44. "severity": "safe"
  45. },
  46. "sexual": {
  47. "filtered": false,
  48. "severity": "safe"
  49. },
  50. "violence": {
  51. "filtered": false,
  52. "severity": "safe"
  53. }
  54. }
  55. }],
  56. "usage": {
  57. "prompt_tokens": 826,
  58. "completion_tokens": 175,
  59. "total_tokens": 1001
  60. }
  61. }

流式的根据数据内容不同,data 数据不同:

  1. data: {"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_result":{"jailbreak":{"filtered":false,"detected":false},"custom_blocklists":{"filtered":false,"details":[]}}},{"prompt_index":1,"content_filter_result":{"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"},"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"custom_blocklists":{"filtered":false,"details":[]}}}]}
  2. data: {"choices":[{"content_filter_results":{},"delta":{"content":"","role":"assistant"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  3. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"镜"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  4. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"腿"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  5. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  6. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"眼"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  7. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"镜"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  8. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"框"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  9. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  10. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"控制"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  11. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"电"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  12. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"路"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  13. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  14. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"连接"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  15. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"电"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  16. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"路"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  17. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  18. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"前"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  19. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"顶"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  20. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"盖"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  21. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  22. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"摄"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  23. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"像"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  24. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"头"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  25. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  26. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"投"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  27. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"影"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  28. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"模块"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  29. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  30. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"鼻"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  31. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"梁"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  32. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  33. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"显示"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  34. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"光"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  35. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"学"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  36. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"元"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  37. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"件"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  38. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"\n"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  39. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"麦"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  40. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"克"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  41. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"风"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  42. data: {"choices":[{"content_filter_results":{},"delta":{},"finish_reason":"stop","index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  43. data: [DONE]

二、实现原理

1、官网申请得到对应使用 GPT4o 模型的相关key 等信息(和之前申请GPT3.5、GPT4 类似)

2、使用 HttpClinet 进行网络 Post 请求,且使用异步请求 ,使用 cancellationToken 用于取消请求

  1. // 发送请求并获取响应
  2. HttpResponseMessage response = await client.SendAsync(request, cancellationToken);

3、根据请求流式和非流式的标志 “stream”,false 为非流式,true 为流式返回数据

4、其中 HttpClient 的异步请求也是可以打断的

 cancellationTokenSource.Cancel();

三、注意实现

1、GPT4o 支持文本和图片数据发起 Post 请求访问,其中 图片数据,可以是 url ,也可以是 Base64 ,其中 Base64 数据注意添加上前缀,例如 png 图片,前缀:data:image/png;base64,

  1. string IMAGE_png_EXTRA_PREFIXES_BASE64 = "data:image/png;base64,";
  2. string base64 = await ImageLoader.Instance.LoadImageAndConvertToBase64();
  3. base64 = IMAGE_png_EXTRA_PREFIXES_BASE64 + base64;

2、这里没有添加历史数据,需要的话可以自动添加历史数据,添加到请求的 public List<Message> messages; 列表中即可,不过,提问要放到最后

  1. public class JsonRequestStruct
  2. {
  3. public int max_tokens;
  4. public bool stream;
  5. public float temperature;
  6. public List<Message> messages;
  7. }
  8. public class Message
  9. {
  10. public string role;
  11. public object content; //这个是不规则的json 列表
  12. }

四、简单效果预览

五、案例简单实现步骤

1、新建 Unity 工程

2、创建接口脚本,定义一些基本功能和回调

3、创建基类,HttpClient 实现数据的请求,取消请求的基本功能

4、创建实现了,填写自己的 GPT4o key 相关信息

5、测试接口,先测试非流式的文字请求

6、测试流式,图片请求的接口

六、关键代码

1、IAzureGpt4o

  1. using System;
  2. using System.Threading.Tasks;
  3. public interface IAzureGpt4o
  4. {
  5. /// <summary>
  6. /// 请求失败的回调
  7. /// </summary>
  8. Action<string> OnSendRequestFailed { get; set; }
  9. /// <summary>
  10. /// 响应的流式数据
  11. /// </summary>
  12. Action<string> OnResponsingData { get; set; }
  13. /// <summary>
  14. /// 响应接收完的数据
  15. /// </summary>
  16. Action<string> OnResponseFinished { get; set; }
  17. /// <summary>
  18. /// 发送请求
  19. /// </summary>
  20. /// <param name="askContent"></param>
  21. /// <param name="imgData">图片数据,支持 url/base64</param>
  22. /// <returns></returns>
  23. Task<string> SendRequestAsync(string askContent, string imgData);
  24. /// <summary>
  25. /// 发送请求(流式返回数据)
  26. /// </summary>
  27. /// <param name="askContent"></param>
  28. /// <param name="imgData">图片数据,支持 url/base64</param>
  29. /// <returns></returns>
  30. Task<string> SendRequestStreamResponseAsync(string askContent, string imgData);
  31. /// <summary>
  32. /// Helper function to cancel the ongoing request
  33. /// </summary>
  34. public void CancelRequest();
  35. }

2、BaseAzureGpt4o

  1. using Newtonsoft.Json;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Net;
  5. using System.Text.RegularExpressions;
  6. using System.Text;
  7. using UnityEngine;
  8. using System;
  9. using System.Threading.Tasks;
  10. using System.Threading;
  11. using System.Net.Http;
  12. /// <summary>
  13. /// GPT 4o 基类
  14. /// </summary>
  15. /// <typeparam name="T"></typeparam>
  16. public class BaseAzureGpt4o : IAzureGpt4o
  17. {
  18. #region Data
  19. /// <summary>
  20. /// TAG
  21. /// </summary>
  22. protected virtual string TAG { get; } = "[BaseAzureGpt4o]";
  23. /// <summary>
  24. /// API_BASE
  25. /// </summary>
  26. protected virtual string API_BASE { get; } = "https://gpt4o-version.openai.azure.com/";
  27. /// <summary>
  28. /// DEPLOYMENT_NAME
  29. /// </summary>
  30. protected virtual string DEPLOYMENT_NAME { get; } = "Your_Deplyment_Name";
  31. /// <summary>
  32. /// API_KEY
  33. /// </summary>
  34. protected virtual string API_KEY { get; } = "YOur_API_Key";
  35. /// <summary>
  36. /// 对应模型接口的发行版本
  37. /// </summary>
  38. protected virtual string MODEL_VERSION { get; } = "2024-05-01-preview";
  39. /// <summary>
  40. /// 组织基础的 Url
  41. /// </summary>
  42. protected virtual string m_BaseUrl { get{ return $"{API_BASE}/openai/deployments/{DEPLOYMENT_NAME}"; } }
  43. /// <summary>
  44. /// 组成最后访问的 Url
  45. /// </summary>
  46. protected virtual string m_EndPointend { get {return $"{m_BaseUrl}/chat/completions?api-version={MODEL_VERSION}"; } }
  47. /// <summary>
  48. /// 最大的 Tokens
  49. /// </summary>
  50. protected virtual int MAX_TOKENS { get; } = 100;
  51. /// <summary>
  52. /// 请求失败的回调
  53. /// </summary>
  54. public Action<string> OnSendRequestFailed { get; set; }
  55. /// <summary>
  56. /// 响应的流式数据
  57. /// </summary>
  58. public Action<string> OnResponsingData { get; set; }
  59. /// <summary>
  60. /// 响应接收完的数据
  61. /// </summary>
  62. public Action<string> OnResponseFinished { get; set; }
  63. /// <summary>
  64. /// CancellationTokenSource
  65. /// </summary>
  66. private CancellationTokenSource cancellationTokenSource;
  67. /// <summary>
  68. /// HttpClient
  69. /// </summary>
  70. private static readonly HttpClient client = new HttpClient();
  71. #endregion
  72. #region Inteface function
  73. /// <summary>
  74. /// 发送请求(正常返回数据)
  75. /// </summary>
  76. /// <param name="askContent"></param>
  77. /// <param name="imgData">图片数据,支持 url/base64</param>
  78. /// <returns></returns>
  79. public virtual async Task<string> SendRequestAsync(string askContent, string imgData)
  80. {
  81. Debug.Log(TAG + "Start HttpWebRequest m_EndPointend : " + m_EndPointend);
  82. cancellationTokenSource = new CancellationTokenSource();
  83. CancellationToken cancellationToken = cancellationTokenSource.Token;
  84. // json 数据
  85. string jsonData = GetJsonStr(askContent, imgData);
  86. /// 设置请求头
  87. client.DefaultRequestHeaders.Clear();
  88. client.DefaultRequestHeaders.Add("api-key", API_KEY);
  89. // 创建 HTTP 内容
  90. var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
  91. try
  92. {
  93. // 创建请求
  94. var request = new HttpRequestMessage(HttpMethod.Post, m_EndPointend)
  95. {
  96. Content = content
  97. };
  98. // 发送请求并获取响应
  99. HttpResponseMessage response = await client.SendAsync(request, cancellationToken);
  100. // 检查响应状态码
  101. if (response.IsSuccessStatusCode)
  102. {
  103. // 读取整个响应内容
  104. string responseText = await response.Content.ReadAsStringAsync();
  105. if (cancellationToken.IsCancellationRequested)
  106. {
  107. Debug.Log(TAG + "Operation was canceled.");
  108. OnSendRequestFailed?.Invoke("Operation was canceled.");
  109. return null; // 或抛出异常,根据需要处理
  110. }
  111. Debug.Log(TAG + " PostRequestStreamToStringAsync whole stream data : " + responseText);
  112. string jsonString = DecodeUnicode(responseText);
  113. string parseWholeRlt;
  114. // 使用 Newtonsoft.Json 将 JSON 字符串映射到对应的对象
  115. JsonResponseStruct data = JsonConvert.DeserializeObject<JsonResponseStruct>(jsonString);
  116. if (data != null)
  117. {
  118. parseWholeRlt = data.choices[0].message.content.ToString();
  119. Debug.Log(TAG + " PostRequestStreamToStringAsync whole stream data : " + parseWholeRlt);
  120. }
  121. else
  122. {
  123. Debug.Log(TAG + " PostRequestStreamToStringAsync whole stream data : " + jsonString);
  124. parseWholeRlt = jsonString;
  125. }
  126. if (OnResponseFinished != null)
  127. {
  128. Debug.Log(TAG + " PostRequestStreamToStringAsync OnResponseFinished is not null ");
  129. OnResponseFinished?.Invoke(parseWholeRlt);
  130. }
  131. else
  132. {
  133. Debug.Log(TAG + " PostRequestStreamToStringAsync OnResponseFinished is null ");
  134. }
  135. return parseWholeRlt;
  136. }
  137. else
  138. {
  139. // 打印错误状态码
  140. Debug.LogError(TAG + "Error: " + response.StatusCode);
  141. OnSendRequestFailed?.Invoke(response.Content.ToString());
  142. return null;
  143. }
  144. }
  145. catch (OperationCanceledException e)
  146. {
  147. Debug.Log(TAG + "Operation was canceled. " + e.Message);
  148. OnSendRequestFailed?.Invoke(e.Message);
  149. return null; // 或抛出异常,根据需要处理
  150. }
  151. catch (WebException e)
  152. {
  153. Debug.Log(TAG + " e.Message: " + e.Message);
  154. OnSendRequestFailed?.Invoke(e.Message);
  155. return null;
  156. }
  157. }
  158. /// <summary>
  159. /// 发送请求(流式返回数据)
  160. /// </summary>
  161. /// <param name="askContent"></param>
  162. /// <param name="imgData">图片数据,支持 url/base64</param>
  163. /// <returns></returns>
  164. public virtual async Task<string> SendRequestStreamResponseAsync(string askContent, string imgData) {
  165. Debug.Log(TAG + "Start HttpWebRequest m_EndPointend : " + m_EndPointend);
  166. cancellationTokenSource = new CancellationTokenSource();
  167. CancellationToken cancellationToken = cancellationTokenSource.Token;
  168. // json 数据
  169. string jsonData = GetJsonStr(askContent, imgData, true);
  170. /// 设置请求头
  171. client.DefaultRequestHeaders.Clear();
  172. client.DefaultRequestHeaders.Add("api-key", API_KEY);
  173. // 创建 HTTP 内容
  174. var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
  175. try
  176. {
  177. // 创建请求
  178. var request = new HttpRequestMessage(HttpMethod.Post, m_EndPointend)
  179. {
  180. Content = content
  181. };
  182. // 发送请求并获取响应头
  183. HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
  184. // 检查响应状态码
  185. if (response.IsSuccessStatusCode)
  186. {
  187. // 以流的方式读取响应内容
  188. using (var stream = await response.Content.ReadAsStreamAsync())
  189. {
  190. using (var reader = new StreamReader(stream))
  191. {
  192. StringBuilder sb = new StringBuilder();
  193. string line;
  194. while ((line = await reader.ReadLineAsync()) != null)
  195. {
  196. string parseRlt = HandleStreamData(line);
  197. if (string.IsNullOrEmpty(parseRlt) == false)
  198. {
  199. OnResponsingData?.Invoke(parseRlt);
  200. }
  201. sb.AppendLine(line); // 逐行读取并累积响应数据
  202. Debug.Log(TAG + " PostRequestStreamToStringAsync getting steam data : " + sb.ToString());
  203. // Check for cancellation
  204. if (cancellationToken.IsCancellationRequested)
  205. {
  206. Debug.Log(TAG + "Operation was canceled.");
  207. return null; // or throw an exception, handle as needed
  208. }
  209. }
  210. Debug.Log(TAG + " PostRequestStreamToStringAsync whole stream data : " + sb.ToString());
  211. string jsonString = DecodeUnicode(sb.ToString());
  212. string parseWholeRlt = HandleStreamData(jsonString);
  213. Debug.Log(TAG + " PostRequestStreamToStringAsync whole stream data : " + parseWholeRlt);
  214. if (OnResponseFinished != null)
  215. {
  216. Debug.Log(TAG + " PostRequestStreamToStringAsync OnResponseFinished is not null ");
  217. OnResponseFinished?.Invoke(parseWholeRlt);
  218. }
  219. else
  220. {
  221. Debug.Log(TAG + " PostRequestStreamToStringAsync OnResponseFinished is null ");
  222. }
  223. return parseWholeRlt;
  224. }
  225. }
  226. }
  227. else
  228. {
  229. // 打印错误状态码
  230. Debug.LogError(TAG + "Error: " + response.StatusCode);
  231. OnSendRequestFailed?.Invoke(response.Content.ToString());
  232. return null;
  233. }
  234. }
  235. catch (OperationCanceledException e)
  236. {
  237. Debug.Log(TAG + "Operation was canceled. " + e.Message);
  238. OnSendRequestFailed?.Invoke(e.Message);
  239. return null; // or throw an exception, handle as needed
  240. }
  241. catch (WebException e)
  242. {
  243. Debug.Log(TAG + " e.Message: " + e.Message);
  244. OnSendRequestFailed?.Invoke(e.Message);
  245. return null;
  246. }
  247. }
  248. /// <summary>
  249. /// Helper function to cancel the ongoing request
  250. /// </summary>
  251. public virtual void CancelRequest()
  252. {
  253. if (cancellationTokenSource != null && !cancellationTokenSource.IsCancellationRequested)
  254. {
  255. cancellationTokenSource.Cancel();
  256. }
  257. }
  258. #endregion
  259. #region Protected function
  260. /// <summary>
  261. /// 将 Unicode 转义字符解码为 UTF-8 字符串的方法
  262. /// </summary>
  263. /// <param name="input"></param>
  264. /// <returns></returns>
  265. protected string DecodeUnicode(string input)
  266. {
  267. return Regex.Replace(input, @"\\u([0-9a-fA-F]{4})", match =>
  268. {
  269. string hex = match.Groups[1].Value;
  270. int codePoint = Convert.ToInt32(hex, 16);
  271. return char.ConvertFromUtf32(codePoint);
  272. });
  273. }
  274. /// <summary>
  275. /// 组织提问和图片数据
  276. /// </summary>
  277. /// <param name="askContent"></param>
  278. /// <param name="imgBase64OrUrlg"></param>
  279. /// <param name="isStreamResponse">是否流式响应返回数据</param>
  280. /// <returns></returns>
  281. protected string GetJsonStr(string askContent, string imgBase64OrUrlg, bool isStreamResponse = false)
  282. {
  283. List<object> contentList = new List<object>();
  284. contentList.Add(new
  285. {
  286. type = "text",
  287. text = askContent,
  288. });
  289. if (imgBase64OrUrlg != null)
  290. {
  291. // 创建一个包含指定数据结构的对象列表
  292. contentList.Add(new
  293. {
  294. type = "image_url",
  295. image_url = new
  296. {
  297. url = imgBase64OrUrlg
  298. },
  299. });
  300. }
  301. // 将对象列表转换为 JSON 字符串
  302. string jsonString = JsonConvert.SerializeObject(contentList, Formatting.Indented);
  303. Message messageS = new Message() { role = "system", content = "You are a helpful assistant." };
  304. Message message = new Message() { role = "user", content = contentList };
  305. List<Message> messages = new List<Message>();
  306. messages.Add(messageS);
  307. messages.Add(message);
  308. JsonRequestStruct jsonRequest = new JsonRequestStruct() { messages = messages, max_tokens = MAX_TOKENS, stream = isStreamResponse };
  309. jsonString = JsonConvert.SerializeObject(jsonRequest, Formatting.Indented);
  310. Debug.Log(jsonString);
  311. return jsonString;
  312. }
  313. #region Stream Response 数据解析
  314. /*
  315. data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"克"},"finish_reason":null,"index":0,"logprobs":null}],"created":1718690676,"id":"chatcmpl-9bMFIE4gPNKU6Z1QyKvFsjwqNTOtw","model":"gpt-4o-2024-05-13","object":"chat.completion.chunk","system_fingerprint":"fp_abc28019ad"}
  316. */
  317. /// <summary>
  318. /// 流式处理数据
  319. /// </summary>
  320. /// <param name="data"></param>
  321. /// <returns></returns>
  322. string HandleStreamData(string data)
  323. {
  324. try
  325. {
  326. string[] dataLines = data.Split(new string[] { "data: " }, StringSplitOptions.RemoveEmptyEntries);
  327. StringBuilder sb = new StringBuilder();
  328. foreach (var line in dataLines)
  329. {
  330. sb.Append(ProcessDataLine(line));
  331. }
  332. return sb.ToString();
  333. }
  334. catch (Exception e)
  335. {
  336. return null;
  337. }
  338. }
  339. /// <summary>
  340. /// 处理单行数据,解析获取 content,finish_reason 数据
  341. /// content : 回复内容
  342. /// finish_reason : 判断流式回答是否结束
  343. /// </summary>
  344. /// <param name="dataLine"></param>
  345. /// /// <returns></returns>
  346. string ProcessDataLine(string dataLine)
  347. {
  348. string content = ExtractField(dataLine, "\"content\":\"", "\"},");
  349. string finishReason = ExtractField(dataLine, "\"finish_reason\":", ",");
  350. Debug.Log(TAG + "ProcessDataLine():Content: " + content);
  351. content = content == null ? "" : content;
  352. // 排除这个情况 "delta":{"content":"","role":"assistant"},"finish_reason":null,"
  353. if(content.Contains("\"role\":\"assistant")) content="";
  354. if (finishReason!=null && finishReason == "\"stop\"")
  355. {
  356. Debug.Log(TAG+ "ProcessDataLine(): Finish reason is stop. Stopping further processing.");
  357. return ""; // 如果 finish_reason 是 stop,停止进一步处理
  358. }
  359. else
  360. {
  361. return content;
  362. }
  363. }
  364. /// <summary>
  365. /// 抽取块数据
  366. /// </summary>
  367. /// <param name="dataLine">行数据</param>
  368. /// <param name="fieldName">块名称</param>
  369. /// <param name="delimiter">分割符</param>
  370. /// <returns></returns>
  371. string ExtractField(string dataLine, string fieldName, string delimiter)
  372. {
  373. int fieldIndex = dataLine.IndexOf(fieldName);
  374. if (fieldIndex == -1)
  375. {
  376. return null;
  377. }
  378. int startIndex = fieldIndex + fieldName.Length;
  379. int endIndex = dataLine.IndexOf(delimiter, startIndex);
  380. if (endIndex == -1)
  381. {
  382. return dataLine.Substring(startIndex).Trim(' ', '}', ']');
  383. }
  384. return dataLine.Substring(startIndex, endIndex - startIndex);
  385. }
  386. #endregion
  387. #endregion
  388. #region JsonRequestStruct
  389. /* json 字符串
  390. {
  391. "messages": [
  392. { "role": "system", "content": "You are a helpful assistant." }, # Content can be a string, OR
  393. { "role": "user", "content": [ # It can be an array containing strings and images.
  394. "描述一下这张图,请用中文回答",
  395. { "image": "img_base64" } # Images are represented like this.
  396. ] }
  397. ],
  398. "max_tokens": 100
  399. }
  400. */
  401. /* 更新 json 字符串
  402. {
  403. "messages": [
  404. {
  405. "role": "system",
  406. "content": "You are a helpful assistant."
  407. },
  408. {
  409. "role": "user",
  410. "content": [
  411. {
  412. "type": "text",
  413. "text": "Describe this picture:"
  414. },
  415. {
  416. "type": "image_url",
  417. "image_url": {
  418. "url": "<image URL>"
  419. }
  420. }
  421. ]
  422. }
  423. ],
  424. "max_tokens": 100,
  425. "stream": false
  426. }
  427. */
  428. public class JsonRequestStruct
  429. {
  430. public int max_tokens;
  431. public bool stream;
  432. public float temperature;
  433. public List<Message> messages;
  434. }
  435. public class Message
  436. {
  437. public string role;
  438. public object content; //这个是不规则的json 列表
  439. }
  440. #endregion
  441. #region JsonResponseStruct
  442. /* json 字符串
  443. {
  444. "id": "chatcmpl-8X0uY6Xbv4XpU4zo0KXhVk4iTldJf",
  445. "object": "chat.completion",
  446. "created": 1702879018,
  447. "model": "gpt-4v",
  448. "prompt_filter_results": [{
  449. "prompt_index": 0,
  450. "content_filter_results": {
  451. "hate": {
  452. "filtered": false,
  453. "severity": "safe"
  454. },
  455. "self_harm": {
  456. "filtered": false,
  457. "severity": "safe"
  458. },
  459. "sexual": {
  460. "filtered": false,
  461. "severity": "safe"
  462. },
  463. "violence": {
  464. "filtered": false,
  465. "severity": "safe"
  466. }
  467. }
  468. }],
  469. "choices": [{
  470. "finish_details": {
  471. "type": "stop",
  472. "stop": "<|fim_suffix|>"
  473. },
  474. "index": 0,
  475. "message": {
  476. "role": "assistant",
  477. "content": "这张图片展示了一辆银灰色的跑车,停靠在一个内饰精致的展厅里。车身呈流线型,前脸上有显著的品牌标志和数字“10”。车身上的漆面光洁明亮,反射着展厅内柔和的灯光。背景中可以看到其他几辆停放整齐的高端车辆,以及墙上挂着的一些装饰性画作。整个场景显得高端大气,彰显了跑车的豪华品质。"
  478. },
  479. "content_filter_results": {
  480. "hate": {
  481. "filtered": false,
  482. "severity": "safe"
  483. },
  484. "self_harm": {
  485. "filtered": false,
  486. "severity": "safe"
  487. },
  488. "sexual": {
  489. "filtered": false,
  490. "severity": "safe"
  491. },
  492. "violence": {
  493. "filtered": false,
  494. "severity": "safe"
  495. }
  496. }
  497. }],
  498. "usage": {
  499. "prompt_tokens": 826,
  500. "completion_tokens": 175,
  501. "total_tokens": 1001
  502. }
  503. }
  504. */
  505. public class JsonResponseStruct
  506. {
  507. public string id { get; set; }
  508. public string @object { get; set; }
  509. public long created { get; set; }
  510. public string model { get; set; }
  511. public Usage usage { get; set; }
  512. public List<Choice> choices { get; set; }
  513. public List<PromptFilterResult> prompt_filter_results { get; set; }
  514. }
  515. public class Usage
  516. {
  517. public int prompt_tokens { get; set; }
  518. public int completion_tokens { get; set; }
  519. public int total_tokens { get; set; }
  520. }
  521. public class Choice
  522. {
  523. public RMessage message { get; set; }
  524. public FinishDetails finish_details { get; set; }
  525. public int index { get; set; }
  526. public ContentFilterResult content_filter_results { get; set; }
  527. }
  528. public class RMessage
  529. {
  530. public string role;
  531. public string content; //这个是不规则的json 列表
  532. }
  533. public class FinishDetails
  534. {
  535. public string type { get; set; }
  536. public string stop { get; set; }
  537. }
  538. public class PromptFilterResult
  539. {
  540. public int index { get; set; }
  541. public ContentFilterResult content_filter_results { get; set; }
  542. }
  543. public class ContentFilterResult
  544. {
  545. public FilterReuslt hate { get; set; }
  546. public FilterReuslt self_harm { get; set; }
  547. public FilterReuslt sexual { get; set; }
  548. public FilterReuslt violence { get; set; }
  549. }
  550. public class FilterReuslt
  551. {
  552. public bool filtered { get; set; }
  553. public string severity { get; set; }
  554. }
  555. #endregion
  556. }

3、AzureGPT4o

  1. public class AzureGPT4o : BaseAzureGpt4o
  2. {
  3. /// <summary>
  4. /// TAG
  5. /// </summary>
  6. protected override string TAG { get; } = "[AzureGPT4o]";
  7. /// <summary>
  8. /// API_BASE
  9. /// </summary>
  10. protected override string API_BASE { get; } = "https://gpt4o-version.openai.azure.com/";
  11. /// <summary>
  12. /// DEPLOYMENT_NAME
  13. /// </summary>
  14. protected override string DEPLOYMENT_NAME { get; } = "Your_Deployment_Name";
  15. /// <summary>
  16. /// API_KEY
  17. /// </summary>
  18. protected override string API_KEY { get; } = "Your_API_Key";
  19. /// <summary>
  20. /// 对应模型接口的发行版本
  21. /// </summary>
  22. protected override string MODEL_VERSION { get; } = "2024-05-01-preview";
  23. /// <summary>
  24. /// 最大的 Tokens
  25. /// </summary>
  26. protected override int MAX_TOKENS { get; } = 300;
  27. }

4、TestAzureGPT4o

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class TestAzureGPT4o : MonoBehaviour
  5. {
  6. string TAG = "[TestAzureGPT4o] ";
  7. IAzureGpt4o m_AzureGpt4o;
  8. // Start is called before the first frame update
  9. void Start()
  10. {
  11. //TestGPT4o_Text();
  12. TestGPT4o_Image();
  13. }
  14. // Update is called once per frame
  15. void Update()
  16. {
  17. if (Input.GetKeyDown(KeyCode.Space)) {
  18. m_AzureGpt4o?.CancelRequest();
  19. }
  20. }
  21. async void TestGPT4o_Text()
  22. {
  23. m_AzureGpt4o = new AzureGPT4o();
  24. m_AzureGpt4o.OnSendRequestFailed = (err) => { Debug.Log(TAG + "TestGPT4o_Text(): OnSendRequestFailed err " + err); };
  25. m_AzureGpt4o.OnResponseFinished = (str) => { Debug.Log(TAG + "TestGPT4o_Text(): OnResponseFinished str " + str); };
  26. m_AzureGpt4o.OnResponsingData = (str) => { Debug.Log(TAG + "TestGPT4o_Text(): OnResponsingData str " + str); };
  27. string rlt = await m_AzureGpt4o.SendRequestAsync(
  28. "你好呀,你是谁?",
  29. null);
  30. Debug.Log(TAG + "TestGPT4o_Text(): rlt " + rlt);
  31. }
  32. async void TestGPT4o_Image()
  33. {
  34. m_AzureGpt4o = new AzureGPT4o();
  35. m_AzureGpt4o.OnSendRequestFailed = (err) => { Debug.Log(TAG + "TestGPT4o_Image(): OnSendRequestFailed err " + err); };
  36. m_AzureGpt4o.OnResponseFinished = (str) => { Debug.Log(TAG + "TestGPT4o_Image(): OnResponseFinished str " + str); };
  37. m_AzureGpt4o.OnResponsingData = (str) => { Debug.Log(TAG + "TestGPT4o_Image(): OnResponsingData str " + str); };
  38. string rlt = await m_AzureGpt4o.SendRequestStreamResponseAsync(
  39. "描述一下",
  40. "https://d1.faiusr.com/4/AAEIABAEGAAggOra9AUooNDeiAEwoAY45gM.png");
  41. Debug.Log(TAG + "TestGPT4o_Image(): rlt " + rlt);
  42. }
  43. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号