Commit 5dc93cb872c4563786293c0e1299b14effb5e00e

Authored by 芋道源码
Committed by Gitee
2 parents acf68b1c ecf4df86

!1294 【功能新增】AI:画图通用功能增加硅基流动平台

Merge pull request !1294 from 拽拽的哥/feature/ai
Showing 23 changed files with 958 additions and 11 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO;
  6 +import cn.iocoder.yudao.module.ai.service.creation.AiartCreationService;
  7 +import io.swagger.v3.oas.annotations.Operation;
  8 +import io.swagger.v3.oas.annotations.tags.Tag;
  9 +import jakarta.annotation.Resource;
  10 +import jakarta.validation.Valid;
  11 +import org.springframework.validation.annotation.Validated;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestBody;
  14 +import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +
  20 +@Tag(name = "管理后台 - 艺术创作")
  21 +@RestController
  22 +@RequestMapping("/ai/artcreation")
  23 +@Validated
  24 +public class AiartCreationController {
  25 +
  26 + @Resource
  27 + private AiartCreationService aiartCreationService;
  28 +
  29 + @Operation(summary = "商品图替换背景")
  30 + @PostMapping("/replacebackground")
  31 + public CommonResult<AiartReplaceBackgroundResVO> replaceBackground(@Valid @RequestBody AiartReplaceBackgroundReqVO replaceBackgroundReqVO) {
  32 + return success(aiartCreationService.replaceBackground(getLoginUserId(), replaceBackgroundReqVO));
  33 + }
  34 +
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO;
  8 +import cn.iocoder.yudao.module.ai.service.creation.AiartStyleService;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +import io.swagger.v3.oas.annotations.tags.Tag;
  11 +import jakarta.annotation.Resource;
  12 +import jakarta.validation.Valid;
  13 +import org.springframework.security.access.prepost.PreAuthorize;
  14 +import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.PostMapping;
  16 +import org.springframework.web.bind.annotation.RequestBody;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +
  24 +@Tag(name = "管理后台 - 艺术创作模版")
  25 +@RestController
  26 +@RequestMapping("/ai/artstyle")
  27 +@Validated
  28 +public class AiartStyleController {
  29 +
  30 + @Resource
  31 + private AiartStyleService aiartStyleService;
  32 +
  33 + @PostMapping("/backgroundtemplate-list")
  34 + @Operation(summary = "获取商品背景模版列表")
  35 + @PreAuthorize("@ss.hasPermission('ai:artstyle-background:query')")
  36 + public CommonResult<List<BackgroundTemplateResVO>> queryBackgroundStyle(@Valid @RequestBody BackgroundTemplateReqVO backgroundTemplateReqVO) {
  37 + List<AiartStyleBackgroundTemplateDO> res = aiartStyleService.queryBackgroundStyle(backgroundTemplateReqVO);
  38 + return success(BeanUtils.toBean(res,BackgroundTemplateResVO.class));
  39 + }
  40 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.Size;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - 商品替换背景请求 VO")
  9 +@Data
  10 +public class AiartReplaceBackgroundReqVO {
  11 +
  12 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
  13 + @NotEmpty(message = "提示词不能为空")
  14 + @Size(max = 1200, message = "提示词最大 1200")
  15 + private String prompt;
  16 +
  17 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - 商品替换背景结果 VO")
  7 +@Data
  8 +public class AiartReplaceBackgroundResVO {
  9 +
  10 + @Schema(description = "结果url", requiredMode = Schema.RequiredMode.REQUIRED, example = "根据参数不同不一样")
  11 + private String resultImage;
  12 +
  13 + @Schema(description = "唯一请求 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "由服务端生成")
  14 + private String requestId;
  15 +
  16 + @Schema(description = "MaskUrl", requiredMode = Schema.RequiredMode.REQUIRED, example = "如果 MaskUrl 未传,则返回使用内置商品分割算法得到的 Mask 结果")
  17 + private String maskImage;
  18 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO")
  7 +@Data
  8 +public class BackgroundTemplateReqVO {
  9 +
  10 + @Schema(description = "一级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "通用")
  11 + private String fistLevelName;
  12 +
  13 + @Schema(description = "二级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "色彩背景")
  14 + private String secondLevelName;
  15 +
  16 +}
0 17 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - 商品替换背景模版 VO")
  7 +@Data
  8 +public class BackgroundTemplateResVO {
  9 +
  10 + @Schema(description = "一级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "汽车、通用")
  11 + private String levelFirst;
  12 +
  13 + @Schema(description = "二级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "色彩背景")
  14 + private String levelSecond;
  15 +
  16 + @Schema(description = "背景模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "彩虹渐变")
  17 + private String name;
  18 +
  19 + @Schema(description = "背景模版提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "背景为彩虹渐变墙面,亮暖色,清新简约风格,喷涂效果")
  20 + private String prompt;
  21 +
  22 + @Schema(description = "背景模版商品示例", requiredMode = Schema.RequiredMode.REQUIRED, example = "小熊玩偶")
  23 + private String demProduct;
  24 +
  25 + @Schema(description = "背景模版商品示例效果", requiredMode = Schema.RequiredMode.REQUIRED, example = "url")
  26 + private String demUrl;
  27 +}
  28 +
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.creation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.AllArgsConstructor;
  9 +import lombok.Builder;
  10 +import lombok.Data;
  11 +import lombok.NoArgsConstructor;
  12 +
  13 +/**
  14 + * AI art商品背景风格 DO
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@TableName("ai_backgroundtemplate")
  19 +@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  20 +@Data
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class AiartStyleBackgroundTemplateDO extends BaseDO {
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 +
  31 + /**
  32 + * 名称
  33 + */
  34 + private String levelFirst;
  35 +
  36 + /**
  37 + * 名称
  38 + */
  39 + private String levelSecond;
  40 + /**
  41 + * 模版名称
  42 + */
  43 + private String name;
  44 + /**
  45 + * 模版提示词
  46 + */
  47 + private String prompt;
  48 + /**
  49 + * 示例商品
  50 + */
  51 + private String demoProduct;
  52 +
  53 + /**
  54 + * 示例效果
  55 + */
  56 + private String demoUrl;
  57 +
  58 + /**
  59 + * 状态
  60 + *
  61 + * 枚举 {@link CommonStatusEnum}
  62 + */
  63 + private Integer status;
  64 +
  65 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.creation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * AI style分隔 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface AiartStyleBackgroundTemplateMapper extends BaseMapperX<AiartStyleBackgroundTemplateDO> {
  19 +
  20 + /**
  21 + * 获取有效的商品后端模版
  22 + * @param reqVO 请求对象
  23 + * @return 结果
  24 + */
  25 + default List<AiartStyleBackgroundTemplateDO> selectList(BackgroundTemplateReqVO reqVO) {
  26 + return selectList(new LambdaQueryWrapperX<AiartStyleBackgroundTemplateDO>()
  27 + .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelFirst,reqVO.getFistLevelName())
  28 + .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelSecond,reqVO.getSecondLevelName())
  29 + .eqIfPresent(AiartStyleBackgroundTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
  30 + .orderByDesc(AiartStyleBackgroundTemplateDO::getId));
  31 + }
  32 +
  33 +}
0 34 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.creation;
  2 +
  3 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO;
  5 +
  6 +/**
  7 + * AI创作Service 接口
  8 + *
  9 + * @author zzt
  10 + */
  11 +public interface AiartCreationService {
  12 +
  13 +
  14 + /**
  15 + * 替换商品背景图片
  16 + * @param loginUserId 当前登录用户
  17 + * @param replaceBackgroundReqVO 请求对象
  18 + * @return 结果
  19 + */
  20 + AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO);
  21 +}
0 22 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.creation;
  2 +
  3 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO;
  5 +import org.springframework.stereotype.Service;
  6 +import org.springframework.validation.annotation.Validated;
  7 +
  8 +/**
  9 + * AI创作Service 实现类
  10 + *
  11 + * @author zzt
  12 + */
  13 +@Service
  14 +@Validated
  15 +public class AiartCreationServiceImpl implements AiartCreationService {
  16 +
  17 +
  18 + @Override
  19 + public AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO) {
  20 + return null;
  21 + }
  22 +}
0 23 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.creation;
  2 +
  3 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * AI风格Service 接口
  11 + *
  12 + * @author zzt
  13 + */
  14 +public interface AiartStyleService {
  15 +
  16 +
  17 + /**
  18 + * 查询商品背景模版列表
  19 + * @param backgroundTemplateReqVO 商品背景模版请求对象
  20 + * @return 结果
  21 + */
  22 + List<AiartStyleBackgroundTemplateDO> queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO);
  23 +}
0 24 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.creation;
  2 +
  3 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO;
  6 +import cn.iocoder.yudao.module.ai.dal.mysql.creation.AiartStyleBackgroundTemplateMapper;
  7 +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
  8 +import jakarta.annotation.Resource;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * AI风格Service 接口
  14 + *
  15 + * @author zzt
  16 + */
  17 +public class AiartStyleServiceImpl implements AiartStyleService{
  18 +
  19 + @Resource
  20 + private AiartStyleBackgroundTemplateMapper backgroundTemplateMapper;
  21 +
  22 +
  23 + @Override
  24 + public List<AiartStyleBackgroundTemplateDO> queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO) {
  25 + return backgroundTemplateMapper.selectList(backgroundTemplateReqVO);
  26 + }
  27 +}
0 28 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
... ... @@ -11,6 +11,7 @@ import cn.hutool.extra.spring.SpringUtil;
11 11 import cn.hutool.http.HttpUtil;
12 12 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
13 13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  14 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageOptions;
14 15 import cn.iocoder.yudao.framework.common.pojo.PageResult;
15 16 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
16 17 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
... ... @@ -144,7 +145,12 @@ public class AiImageServiceImpl implements AiImageService {
144 145 .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
145 146 .withResponseFormat("b64_json")
146 147 .build();
147   - } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
  148 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) {
  149 + // https://docs.siliconflow.cn/cn/api-reference/images/images-generations
  150 + return SiliconflowImageOptions.builder().withModel(model.getModel())
  151 + .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  152 + .build();
  153 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
148 154 // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
149 155 // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
150 156 return StabilityAiImageOptions.builder().model(model.getModel())
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
... ... @@ -117,6 +117,13 @@
117 117 </exclusions>
118 118 </dependency>
119 119  
  120 + <!-- 腾讯大模型图像创作引擎-->
  121 + <dependency>
  122 + <groupId>com.tencentcloudapi</groupId>
  123 + <artifactId>tencentcloud-sdk-java-aiart</artifactId>
  124 + <version>3.1.1215</version>
  125 + </dependency>
  126 +
120 127 <!-- Test 测试相关 -->
121 128 <dependency>
122 129 <groupId>org.springframework.boot</groupId>
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
... ... @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
8 8 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
9 9 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
10 10 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  11 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants;
  12 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi;
11 13 import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
12 14 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
13 15 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
... ... @@ -113,11 +115,11 @@ public class YudaoAiAutoConfiguration {
113 115  
114 116 public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) {
115 117 if (StrUtil.isEmpty(properties.getModel())) {
116   - properties.setModel(SiliconFlowChatModel.MODEL_DEFAULT);
  118 + properties.setModel(SiiconflowApiConstants.MODEL_DEFAULT);
117 119 }
118 120 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
119 121 .openAiApi(OpenAiApi.builder()
120   - .baseUrl(SiliconFlowChatModel.BASE_URL)
  122 + .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL)
121 123 .apiKey(properties.getApiKey())
122 124 .build())
123 125 .defaultOptions(OpenAiChatOptions.builder()
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -15,7 +15,10 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
15 15 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
16 16 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
17 17 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  18 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants;
  19 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi;
18 20 import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
  21 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageModel;
19 22 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
20 23 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
21 24 import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
... ... @@ -224,6 +227,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
224 227 return buildZhiPuAiImageModel(apiKey, url);
225 228 case OPENAI:
226 229 return buildOpenAiImageModel(apiKey, url);
  230 + case SILICON_FLOW:
  231 + return buildSiiconflowImageModel(apiKey,url);
227 232 case STABLE_DIFFUSION:
228 233 return buildStabilityAiImageModel(apiKey, url);
229 234 default:
... ... @@ -469,6 +474,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
469 474 }
470 475  
471 476 /**
  477 + * Siiconflow
  478 + */
  479 + private SiliconflowImageModel buildSiiconflowImageModel(String apiToken, String url) {
  480 + url = StrUtil.blankToDefault(url, SiiconflowApiConstants.DEFAULT_BASE_URL);
  481 + SiiconflowmageApi openAiApi = SiiconflowmageApi.builder().baseUrl(url).apiKey(apiToken).build();
  482 + return new SiliconflowImageModel(openAiApi);
  483 + }
  484 +
  485 + /**
472 486 * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法
473 487 */
474 488 private static OllamaChatModel buildOllamaChatModel(String url) {
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java 0 → 100644
  1 +/*
  2 + * Copyright 2023-2024 the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * https://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package cn.iocoder.yudao.framework.ai.core.model.siliconflow;
  18 +
  19 +/**
  20 + * Common value constants for Siiconflow api.
  21 + *
  22 + * @author zzt
  23 + */
  24 +public final class SiiconflowApiConstants {
  25 +
  26 + public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn";
  27 +
  28 + public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
  29 +
  30 + public static final String PROVIDER_NAME = "Siiconflow";
  31 +
  32 + private SiiconflowApiConstants() {
  33 +
  34 + }
  35 +
  36 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java 0 → 100644
  1 +/*
  2 + * Copyright 2023-2024 the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * https://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package cn.iocoder.yudao.framework.ai.core.model.siliconflow;
  18 +
  19 +import com.fasterxml.jackson.annotation.JsonInclude;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import org.springframework.ai.model.ApiKey;
  22 +import org.springframework.ai.model.NoopApiKey;
  23 +import org.springframework.ai.model.SimpleApiKey;
  24 +import org.springframework.ai.openai.api.OpenAiImageApi;
  25 +import org.springframework.ai.retry.RetryUtils;
  26 +import org.springframework.http.MediaType;
  27 +import org.springframework.http.ResponseEntity;
  28 +import org.springframework.util.Assert;
  29 +import org.springframework.util.CollectionUtils;
  30 +import org.springframework.util.LinkedMultiValueMap;
  31 +import org.springframework.util.MultiValueMap;
  32 +import org.springframework.web.client.ResponseErrorHandler;
  33 +import org.springframework.web.client.RestClient;
  34 +
  35 +import java.util.Map;
  36 +
  37 +/**
  38 + * Siiconflow Image API.
  39 + *
  40 + * @see <a href= "https://docs.siliconflow.cn/cn/api-reference/images/images-generations">Images</a>
  41 + *
  42 + * @author zzt
  43 + */
  44 +public class SiiconflowmageApi {
  45 +
  46 + private final RestClient restClient;
  47 +
  48 + /**
  49 + * Create a new Siiconflow Image api with base URL set.
  50 + * @param aiToken OpenAI apiKey.
  51 + */
  52 + public SiiconflowmageApi(String aiToken) {
  53 + this(SiiconflowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder());
  54 + }
  55 +
  56 + /**
  57 + * Create a new Siiconflow Image API with the provided base URL.
  58 + * @param baseUrl the base URL for the OpenAI API.
  59 + * @param openAiToken Siiconflow apiKey.
  60 + */
  61 + public SiiconflowmageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) {
  62 + this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
  63 + }
  64 +
  65 + /**
  66 + * Create a new OpenAI Image API with the provided base URL.
  67 + * @param baseUrl the base URL for the OpenAI API.
  68 + * @param apiKey OpenAI apiKey.
  69 + * @param restClientBuilder the rest client builder to use.
  70 + */
  71 + public SiiconflowmageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder,
  72 + ResponseErrorHandler responseErrorHandler) {
  73 + this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler);
  74 + }
  75 +
  76 + /**
  77 + * Create a new OpenAI Image API with the provided base URL.
  78 + * @param baseUrl the base URL for the OpenAI API.
  79 + * @param apiKey OpenAI apiKey.
  80 + * @param headers the http headers to use.
  81 + * @param restClientBuilder the rest client builder to use.
  82 + * @param responseErrorHandler the response error handler to use.
  83 + */
  84 + public SiiconflowmageApi(String baseUrl, String apiKey, MultiValueMap<String, String> headers,
  85 + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
  86 +
  87 + this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler);
  88 + }
  89 +
  90 + /**
  91 + * Create a new OpenAI Image API with the provided base URL.
  92 + * @param baseUrl the base URL for the OpenAI API.
  93 + * @param apiKey OpenAI apiKey.
  94 + * @param headers the http headers to use.
  95 + * @param restClientBuilder the rest client builder to use.
  96 + * @param responseErrorHandler the response error handler to use.
  97 + */
  98 + public SiiconflowmageApi(String baseUrl, ApiKey apiKey, MultiValueMap<String, String> headers,
  99 + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
  100 +
  101 + // @formatter:off
  102 + this.restClient = restClientBuilder.baseUrl(baseUrl)
  103 + .defaultHeaders(h -> {
  104 + if(!(apiKey instanceof NoopApiKey)) {
  105 + h.setBearerAuth(apiKey.getValue());
  106 + }
  107 + h.setContentType(MediaType.APPLICATION_JSON);
  108 + h.addAll(headers);
  109 + })
  110 + .defaultStatusHandler(responseErrorHandler)
  111 + .build();
  112 + // @formatter:on
  113 + }
  114 +
  115 + public ResponseEntity<OpenAiImageApi.OpenAiImageResponse> createImage(SiliconflowImageRequest siliconflowImageRequest) {
  116 + Assert.notNull(siliconflowImageRequest, "Image request cannot be null.");
  117 + Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty.");
  118 +
  119 + return this.restClient.post()
  120 + .uri("v1/images/generations")
  121 + .body(siliconflowImageRequest)
  122 + .retrieve()
  123 + .toEntity(OpenAiImageApi.OpenAiImageResponse.class);
  124 + }
  125 +
  126 +
  127 + // @formatter:off
  128 + @JsonInclude(JsonInclude.Include.NON_NULL)
  129 + public record SiliconflowImageRequest (
  130 + @JsonProperty("prompt") String prompt,
  131 + @JsonProperty("model") String model,
  132 + @JsonProperty("batch_size") Integer batchSize,
  133 + @JsonProperty("negative_prompt") String negativePrompt,
  134 + @JsonProperty("seed") Integer seed,
  135 + @JsonProperty("num_inference_steps") Integer numInferenceSteps,
  136 + @JsonProperty("guidance_scale") Float guidanceScale,
  137 + @JsonProperty("image") String image) {
  138 +
  139 + public SiliconflowImageRequest(String prompt, String model) {
  140 + this(prompt, model, null, null, null, null, null, null);
  141 + }
  142 + }
  143 +
  144 + public static Builder builder() {
  145 + return new Builder();
  146 + }
  147 +
  148 + /**
  149 + * Builder to construct {@link SiiconflowmageApi} instance.
  150 + */
  151 + public static class Builder {
  152 +
  153 + private String baseUrl = SiiconflowApiConstants.DEFAULT_BASE_URL;
  154 +
  155 + private ApiKey apiKey;
  156 +
  157 + private MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
  158 +
  159 + private RestClient.Builder restClientBuilder = RestClient.builder();
  160 +
  161 + private ResponseErrorHandler responseErrorHandler = RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER;
  162 +
  163 + public Builder baseUrl(String baseUrl) {
  164 + Assert.hasText(baseUrl, "baseUrl cannot be null or empty");
  165 + this.baseUrl = baseUrl;
  166 + return this;
  167 + }
  168 +
  169 + public Builder apiKey(ApiKey apiKey) {
  170 + Assert.notNull(apiKey, "apiKey cannot be null");
  171 + this.apiKey = apiKey;
  172 + return this;
  173 + }
  174 +
  175 + public Builder apiKey(String simpleApiKey) {
  176 + Assert.notNull(simpleApiKey, "simpleApiKey cannot be null");
  177 + this.apiKey = new SimpleApiKey(simpleApiKey);
  178 + return this;
  179 + }
  180 +
  181 + public Builder headers(MultiValueMap<String, String> headers) {
  182 + Assert.notNull(headers, "headers cannot be null");
  183 + this.headers = headers;
  184 + return this;
  185 + }
  186 +
  187 + public Builder restClientBuilder(RestClient.Builder restClientBuilder) {
  188 + Assert.notNull(restClientBuilder, "restClientBuilder cannot be null");
  189 + this.restClientBuilder = restClientBuilder;
  190 + return this;
  191 + }
  192 +
  193 + public Builder responseErrorHandler(ResponseErrorHandler responseErrorHandler) {
  194 + Assert.notNull(responseErrorHandler, "responseErrorHandler cannot be null");
  195 + this.responseErrorHandler = responseErrorHandler;
  196 + return this;
  197 + }
  198 +
  199 + public SiiconflowmageApi build() {
  200 + Assert.notNull(this.apiKey, "apiKey must be set");
  201 + return new SiiconflowmageApi(this.baseUrl, this.apiKey, this.headers, this.restClientBuilder,
  202 + this.responseErrorHandler);
  203 + }
  204 +
  205 + }
  206 +
  207 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
... ... @@ -20,10 +20,6 @@ import reactor.core.publisher.Flux;
20 20 @RequiredArgsConstructor
21 21 public class SiliconFlowChatModel implements ChatModel {
22 22  
23   - public static final String BASE_URL = "https://api.siliconflow.cn";
24   -
25   - public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
26   -
27 23 /**
28 24 * 兼容 OpenAI 接口,进行复用
29 25 */
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java 0 → 100644
  1 +/*
  2 + * Copyright 2023-2024 the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * https://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package cn.iocoder.yudao.framework.ai.core.model.siliconflow;
  18 +
  19 +import io.micrometer.observation.ObservationRegistry;
  20 +import org.slf4j.Logger;
  21 +import org.slf4j.LoggerFactory;
  22 +import org.springframework.ai.image.*;
  23 +import org.springframework.ai.image.observation.DefaultImageModelObservationConvention;
  24 +import org.springframework.ai.image.observation.ImageModelObservationContext;
  25 +import org.springframework.ai.image.observation.ImageModelObservationConvention;
  26 +import org.springframework.ai.image.observation.ImageModelObservationDocumentation;
  27 +import org.springframework.ai.model.ModelOptionsUtils;
  28 +import org.springframework.ai.openai.api.OpenAiImageApi;
  29 +import org.springframework.ai.openai.api.common.OpenAiApiConstants;
  30 +import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;
  31 +import org.springframework.ai.retry.RetryUtils;
  32 +import org.springframework.http.ResponseEntity;
  33 +import org.springframework.retry.support.RetryTemplate;
  34 +import org.springframework.util.Assert;
  35 +
  36 +import java.util.List;
  37 +
  38 +/**
  39 + * cv openapi图片模型方法
  40 + *
  41 + * @author zzt
  42 + */
  43 +public class SiliconflowImageModel implements ImageModel {
  44 +
  45 + private static final Logger logger = LoggerFactory.getLogger(SiliconflowImageModel.class);
  46 +
  47 + private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention();
  48 +
  49 + /**
  50 + * The default options used for the image completion requests.
  51 + */
  52 + private final SiliconflowImageOptions defaultOptions;
  53 +
  54 + /**
  55 + * The retry template used to retry the OpenAI Image API calls.
  56 + */
  57 + private final RetryTemplate retryTemplate;
  58 +
  59 + /**
  60 + * Low-level access to the OpenAI Image API.
  61 + */
  62 + private final SiiconflowmageApi siiconflowmageApi;
  63 +
  64 + /**
  65 + * Observation registry used for instrumentation.
  66 + */
  67 + private final ObservationRegistry observationRegistry;
  68 +
  69 + /**
  70 + * Conventions to use for generating observations.
  71 + */
  72 + private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
  73 +
  74 + /**
  75 + * Creates an instance of the OpenAiImageModel.
  76 + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with
  77 + * the OpenAI Image API.
  78 + * @throws IllegalArgumentException if openAiImageApi is null
  79 + */
  80 + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi) {
  81 + this(siiconflowmageApi, SiliconflowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);
  82 + }
  83 +
  84 + /**
  85 + * Initializes a new instance of the OpenAiImageModel.
  86 + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with
  87 + * the OpenAI Image API.
  88 + * @param options The OpenAiImageOptions to configure the image model.
  89 + * @param retryTemplate The retry template.
  90 + */
  91 + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate) {
  92 + this(siiconflowmageApi, options, retryTemplate, ObservationRegistry.NOOP);
  93 + }
  94 +
  95 + /**
  96 + * Initializes a new instance of the OpenAiImageModel.
  97 + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with
  98 + * the OpenAI Image API.
  99 + * @param options The OpenAiImageOptions to configure the image model.
  100 + * @param retryTemplate The retry template.
  101 + * @param observationRegistry The ObservationRegistry used for instrumentation.
  102 + */
  103 + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate,
  104 + ObservationRegistry observationRegistry) {
  105 + Assert.notNull(siiconflowmageApi, "OpenAiImageApi must not be null");
  106 + Assert.notNull(options, "options must not be null");
  107 + Assert.notNull(retryTemplate, "retryTemplate must not be null");
  108 + Assert.notNull(observationRegistry, "observationRegistry must not be null");
  109 + this.siiconflowmageApi = siiconflowmageApi;
  110 + this.defaultOptions = options;
  111 + this.retryTemplate = retryTemplate;
  112 + this.observationRegistry = observationRegistry;
  113 + }
  114 +
  115 + @Override
  116 + public ImageResponse call(ImagePrompt imagePrompt) {
  117 + SiiconflowmageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt);
  118 +
  119 + var observationContext = ImageModelObservationContext.builder()
  120 + .imagePrompt(imagePrompt)
  121 + .provider(OpenAiApiConstants.PROVIDER_NAME)
  122 + .requestOptions(imagePrompt.getOptions())
  123 + .build();
  124 +
  125 + return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION
  126 + .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
  127 + this.observationRegistry)
  128 + .observe(() -> {
  129 + ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity = this.retryTemplate
  130 + .execute(ctx -> this.siiconflowmageApi.createImage(imageRequest));
  131 +
  132 + ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);
  133 +
  134 + observationContext.setResponse(imageResponse);
  135 +
  136 + return imageResponse;
  137 + });
  138 + }
  139 +
  140 + private SiiconflowmageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt) {
  141 + String instructions = imagePrompt.getInstructions().get(0).getText();
  142 +
  143 + SiiconflowmageApi.SiliconflowImageRequest imageRequest = new SiiconflowmageApi.SiliconflowImageRequest(instructions,
  144 + imagePrompt.getOptions().getModel());
  145 +
  146 + return ModelOptionsUtils.merge(imagePrompt.getOptions(), imageRequest, SiiconflowmageApi.SiliconflowImageRequest.class);
  147 + }
  148 +
  149 + private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity,
  150 + SiiconflowmageApi.SiliconflowImageRequest siliconflowImageRequest) {
  151 + OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody();
  152 + if (imageApiResponse == null) {
  153 + logger.warn("No image response returned for request: {}", siliconflowImageRequest);
  154 + return new ImageResponse(List.of());
  155 + }
  156 +
  157 + List<ImageGeneration> imageGenerationList = imageApiResponse.data()
  158 + .stream()
  159 + .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()),
  160 + new OpenAiImageGenerationMetadata(entry.revisedPrompt())))
  161 + .toList();
  162 +
  163 + ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created());
  164 + return new ImageResponse(imageGenerationList, openAiImageResponseMetadata);
  165 + }
  166 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.model.siliconflow;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonProperty;
  4 +import lombok.Data;
  5 +import org.springframework.ai.image.ImageOptions;
  6 +import org.springframework.ai.openai.OpenAiImageOptions;
  7 +
  8 +/**
  9 + * 硅基流动画图能力
  10 + *
  11 + * @author zzt
  12 + */
  13 +@Data
  14 +public class SiliconflowImageOptions implements ImageOptions {
  15 +
  16 + @JsonProperty("model")
  17 + private String model;
  18 +
  19 + @JsonProperty("negative_prompt")
  20 + private String negativePrompt;
  21 +
  22 + /**
  23 + * The number of images to generate. Must be between 1 and 4.
  24 + */
  25 + @JsonProperty("image_size")
  26 + private String imageSize;
  27 +
  28 + /**
  29 + * The number of images to generate. Must be between 1 and 4.
  30 + */
  31 + @JsonProperty("batch_size")
  32 + private Integer batchSize = 1;
  33 +
  34 + /**
  35 + * number of inference steps
  36 + */
  37 + @JsonProperty("num_inference_steps")
  38 + private Integer numInferenceSteps = 25;
  39 +
  40 +
  41 + /**
  42 + * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements.
  43 + *
  44 + * Required range: 0 <= x <= 20
  45 + */
  46 + @JsonProperty("guidance_scale")
  47 + private Float guidanceScale = 0.75F;
  48 +
  49 + /**
  50 + * 如果想要每次都生成固定的图片,可以把seed设置为固定值。
  51 + *
  52 + */
  53 + @JsonProperty("seed")
  54 + private Integer seed = (int)(Math.random() * 1_000_000_000);
  55 +
  56 + /**
  57 + * The image that needs to be uploaded should be converted into base64 format.
  58 + */
  59 + @JsonProperty("image")
  60 + private String image;
  61 +
  62 +
  63 + /**
  64 + * 宽
  65 + */
  66 + private Integer width;
  67 +
  68 +
  69 + /**
  70 + * 高
  71 + */
  72 + private Integer height;
  73 +
  74 + public void setHeight(Integer height) {
  75 + this.height = height;
  76 + if (this.width != null && this.height != null) {
  77 + this.imageSize = this.width + "x" + this.height;
  78 + }
  79 + }
  80 +
  81 + public void setWidth(Integer width) {
  82 + this.width = width;
  83 + if (this.width != null && this.height != null) {
  84 + this.imageSize = this.width + "x" + this.height;
  85 + }
  86 + }
  87 +
  88 + /**
  89 + * 硅基流动
  90 + * @return
  91 + */
  92 + public static SiliconflowImageOptions.Builder builder() {
  93 + return new SiliconflowImageOptions.Builder();
  94 + }
  95 +
  96 + @Override
  97 + public String toString() {
  98 +
  99 + return "SiliconflowImageOptions{" + "model='" + getModel() + '\'' + ", batch_size=" + batchSize + ", imageSize=" + imageSize + ", negativePrompt='"
  100 + + negativePrompt + '\'' + '}';
  101 + }
  102 +
  103 + @Override
  104 + public Integer getN() {
  105 + return null;
  106 + }
  107 +
  108 + @Override
  109 + public String getResponseFormat() {
  110 + return null;
  111 + }
  112 +
  113 + @Override
  114 + public String getStyle() {
  115 + return null;
  116 + }
  117 +
  118 + public static class Builder extends OpenAiImageOptions{
  119 +
  120 + private final SiliconflowImageOptions options;
  121 +
  122 + private Builder() {
  123 + this.options = new SiliconflowImageOptions();
  124 + }
  125 +
  126 + public SiliconflowImageOptions.Builder model(String model) {
  127 + this.options.setModel(model);
  128 + return this;
  129 + }
  130 +
  131 + public SiliconflowImageOptions.Builder withBatchSize(Integer batchSize) {
  132 + options.setBatchSize(batchSize);
  133 + return this;
  134 + }
  135 +
  136 + public SiliconflowImageOptions.Builder withModel(String model) {
  137 + options.setModel(model);
  138 + return this;
  139 + }
  140 +
  141 + public SiliconflowImageOptions.Builder withWidth(Integer width) {
  142 + options.setWidth(width);
  143 + return this;
  144 + }
  145 +
  146 + public SiliconflowImageOptions.Builder withHeight(Integer height) {
  147 + options.setHeight(height);
  148 + return this;
  149 + }
  150 +
  151 + public SiliconflowImageOptions.Builder withSeed(Integer seed) {
  152 + options.setSeed(seed);
  153 + return this;
  154 + }
  155 +
  156 + public SiliconflowImageOptions.Builder withNegativePrompt(String negativePrompt) {
  157 + options.setNegativePrompt(negativePrompt);
  158 + return this;
  159 + }
  160 +
  161 + public SiliconflowImageOptions build() {
  162 + return options;
  163 + }
  164 +
  165 + }
  166 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
... ... @@ -50,8 +50,8 @@ public class AiUtils {
50 50 case HUN_YUAN: // 复用 OpenAI 客户端
51 51 case XING_HUO: // 复用 OpenAI 客户端
52 52 case SILICON_FLOW: // 复用 OpenAI 客户端
53   - return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
54   - .toolNames(toolNames).build();
  53 + OpenAiChatOptions.Builder builder = OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens);
  54 + return toolNames == null ? builder.build() : builder.toolNames(toolNames).build();
55 55 case AZURE_OPENAI:
56 56 // TODO 芋艿:貌似没 model 字段???!
57 57 return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens)
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java
1 1 package cn.iocoder.yudao.framework.ai.chat;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants;
3 4 import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
4 5 import org.junit.jupiter.api.Disabled;
5 6 import org.junit.jupiter.api.Test;
... ... @@ -25,11 +26,11 @@ public class SiliconFlowChatModelTests {
25 26  
26 27 private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
27 28 .openAiApi(OpenAiApi.builder()
28   - .baseUrl(SiliconFlowChatModel.BASE_URL)
  29 + .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL)
29 30 .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey
30 31 .build())
31 32 .defaultOptions(OpenAiChatOptions.builder()
32   - .model(SiliconFlowChatModel.MODEL_DEFAULT) // 模型
  33 + .model(SiiconflowApiConstants.MODEL_DEFAULT) // 模型
33 34 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费
34 35 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费
35 36 .temperature(0.7)
... ...