Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add feishu plugin to SAA #149

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions community/plugins/spring-ai-alibaba-starter-plugin-feishu/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
Copy link
Contributor

@yuluo-yx yuluo-yx Dec 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be consistent with the boot version of spring ai alibaba

btw, you can see:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok,I will update this groupid

<relativePath/>
</parent>
<groupId>com.alibaba.cloud.ai.plugin.feishu</groupId>
<artifactId>spring-ai-alibaba-starter-plugin-feishu</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-alibaba-starter-plugin-feishu</name>
<description>FeiShu Tool for Spring AI Alibaba</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencyManagement>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the transitive relationship of mvn, after introducing the above parent, there is no need to introduce it again here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need this dependency. maybe can delete it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but I saw that this dependency was used in the other plugins in the project

<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.larksuite.oapi</groupId>
<artifactId>oapi-sdk</artifactId>
<version>2.3.7</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.alibaba.cloud.ai.plugin.feishu;

import com.google.gson.JsonParser;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you have used jackson. The global situation should be unified. There is no need for gson here. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

import com.lark.oapi.Client;
import com.lark.oapi.core.request.RequestOptions;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.service.docx.v1.model.*;
import com.lark.oapi.service.drive.v1.model.ListFileReq;
import com.lark.oapi.service.drive.v1.model.ListFileResp;
import java.nio.charset.StandardCharsets;
/**
* @author wudihaoke214
* @author <a href="mailto:[email protected]">wudihaoke214</a>
*/
public class FeiShuService {
private final Client client;

public FeiShuService(Client client) {
this.client = client;
}

/**
* 使用tenant_access_token访问[用户身份]
* @param documentId 文档的唯一ID
* @param userAccessToken 用户访问凭证
* @return String 文档纯文本内容
*/
public String getDocumentContentByUser(String documentId,String userAccessToken) throws Exception {
// 创建请求对象
RawContentDocumentReq req = RawContentDocumentReq.newBuilder()
.documentId(documentId)
.lang(0)
.build();

// 发起请求
RawContentDocumentResp resp = client.docx().document().rawContent(req, RequestOptions.newBuilder()
.userAccessToken(userAccessToken)
.build());
// 处理服务端错误
if (!resp.success()) {
System.out.printf("code:%s,msg:%s,reqId:%s, resp:%s%n",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8))));
throw new Exception(resp.getMsg());
}

// 业务数据处理
return Jsons.DEFAULT.toJson(resp.getData());
}

/**
* 使用tenant_access_token访问[应用身份]
* @param documentId 文档的唯一ID
* @param tenantAccessToken 租户访问凭证
* @return String 文档纯文本内容
*/
public String getDocumentContentByTenant(String documentId,String tenantAccessToken) throws Exception {
// 创建请求对象
RawContentDocumentReq req = RawContentDocumentReq.newBuilder()
.documentId(documentId)
.lang(0)
.build();

// 发起请求
RawContentDocumentResp resp = client.docx().document().rawContent(req,RequestOptions.newBuilder()
.tenantAccessToken(tenantAccessToken)
.build());
// 处理服务端错误
if (!resp.success()) {
System.out.printf("code:%s,msg:%s,reqId:%s, resp:%s%n",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8))));
throw new Exception(resp.getMsg());
}

// 业务数据处理
return Jsons.DEFAULT.toJson(resp.getData());
}

/**
* 获取指定用户的文档列表
* @param userAccessToken 用户访问凭证
* @return String 文档列表JSON[token为文档唯一标识]
*/
public String getDocumentListByUser(String userAccessToken) throws Exception {
// 创建请求对象
ListFileReq req = ListFileReq.newBuilder()
.orderBy("EditedTime")
.direction("DESC")
.build();
// 发起请求
ListFileResp resp = client.drive().file().list(req, RequestOptions.newBuilder()
.userAccessToken(userAccessToken)
.build());
// 处理服务端错误
if (!resp.success()) {
System.out.printf("code:%s,msg:%s,reqId:%s, resp:%s%n",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8))));
throw new Exception(resp.getMsg());
}
return Jsons.DEFAULT.toJson(resp.getData());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.alibaba.cloud.ai.plugin.feishu.config;

import com.lark.oapi.Client;
import com.lark.oapi.core.enums.BaseUrlEnum;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.util.Assert;
/**
* @author wudihaoke214
* @author <a href="mailto:[email protected]">wudihaoke214</a>
*/
@Configuration
@EnableConfigurationProperties(FeiShuProperties.class)
public class FeiShuPluginConfiguration {
@Bean
@ConditionalOnMissingBean
@Description("Build FeiShu Client in Spring AI Alibaba")// description
@ConditionalOnProperty(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a plugin, I don’t think we need this configuration. cc @chickenlj

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In reader of Feishu,users need to configure their own appid and appSecret

prefix = FeiShuProperties.FEISHU_PROPERTIES_PREFIX,
name = "enabled",
havingValue = "true"
)
public Client buildDefaultFeiShuClient(FeiShuProperties feiShuProperties) {
Assert.notNull(feiShuProperties.getAppId(), "FeiShu AppId must not be empty");
Assert.notNull(feiShuProperties.getAppSecret(), "FeiShu AppSecret must not be empty");
return Client.newBuilder(feiShuProperties.getAppId(),feiShuProperties.getAppSecret()) // 默认配置为自建应用
.openBaseUrl(BaseUrlEnum.FeiShu) // 设置域名,默认为飞书
.logReqAtDebug(true) // 在 debug 模式下会打印 http 请求和响应的 headers、body 等信息。
.build();
}
//商店应用自行扩展
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.alibaba.cloud.ai.plugin.feishu.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author wudihaoke214
* @author <a href="mailto:[email protected]">wudihaoke214</a>
*/
@ConfigurationProperties(prefix = FeiShuProperties.FEISHU_PROPERTIES_PREFIX)
public class FeiShuProperties {
public static final String FEISHU_PROPERTIES_PREFIX = "spring.ai.alibaba.plugin.feishu";
private Boolean enabled;
private String appId;

private String appSecret;

public Boolean getEnabled() {
return enabled;
}

public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

public String getAppId() {
return appId;
}

public void setAppId(String appId) {
this.appId = appId;
}

public String getAppSecret() {
return appSecret;
}

public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}

@Override
public String toString() {
return "FeiShuProperties{" +
"enabled=" + enabled +
", appId='" + appId + '\'' +
", AppSecret='" + appSecret + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.alibaba.cloud.ai.plugin.feishu.config.FeiShuPluginConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.alibaba.cloud.ai.plugin.feishu;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author wudihaoke214
* @author <a href="mailto:[email protected]">wudihaoke214</a>
*/
@SpringBootTest
class SpringAiAlibabaStarterPluginFeiShuApplicationTests {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no test class, you can delete it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


@Test
void contextLoads() {
}

}
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
<module>community/plugins/spring-ai-alibaba-starter-plugin-gaode</module>
<module>community/plugins/spring-ai-alibaba-starter-plugin-weather</module>
<module>community/plugins/spring-ai-alibaba-starter-plugin-larksuite</module>
<module>community/plugins/spring-ai-alibaba-starter-plugin-feishu</module>

<module>community/document-readers/github-reader</module>
<module>community/document-readers/github-reader</module>
<module>community/document-readers/poi-document-reader</module>
<module>community/document-readers/tencent-cos-reader</module>
</modules>
Expand Down
1 change: 1 addition & 0 deletions spring-ai-alibaba-examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<module>rag-example</module>
<module>output-parser-example</module>
<module>playground-flight-booking</module>
<module>ollama-example</module>
</modules>

<build>
Expand Down
Loading