Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
37e842f
first half of ansa case study
Oct 31, 2024
300fea4
finish first version
Oct 31, 2024
f3938bb
first proofread
Oct 31, 2024
49b2ddc
final small fixes and make investors optional to render
Nov 1, 2024
f2467e2
Ansa edits on case study (#1080)
rsullivan05 Nov 13, 2024
6a3ffc1
add image and final ryan updates
Nov 21, 2024
ecb653d
fix doc
carlosabadia Nov 22, 2024
1b73efb
add svgs
carlosabadia Nov 22, 2024
7890a57
context menu and alert dialog doc updates (#1063)
tgberkeley Nov 1, 2024
56f6840
Pin reflex version to reflex main (#1070)
ElijahAhianyo Nov 5, 2024
68765c8
Add changelog entry for 0.6.4 (#1061)
masenf Nov 5, 2024
53377e7
add docs on rx.field and rx.Field (#1071)
tgberkeley Nov 7, 2024
0239818
dashboard intro tutorial (#1045)
tgberkeley Nov 8, 2024
4025317
@rx.event (#1064)
adhami3310 Nov 8, 2024
b6f0047
modernize global hotkey watcher (#1069)
adhami3310 Nov 8, 2024
12bde07
move docs to use dataclasses (#1072)
adhami3310 Nov 8, 2024
d8ca778
add chat app in one page (#1073)
tgberkeley Nov 8, 2024
d06b88b
waitlist page hosting (#1075)
Alek99 Nov 9, 2024
bed2e77
fix links after rearrange onboarding PR (#1076)
tgberkeley Nov 9, 2024
b8d55db
[HOS-211] Mobile view + docs hosting banner fix (#1078)
carlosabadia Nov 11, 2024
5417aab
fix wrong values to props (#1079)
adhami3310 Nov 11, 2024
27e4bba
preconnet to gfonts + navbar fix (#1054)
carlosabadia Nov 12, 2024
fa57059
fix a typo (#1081)
Lendemor Nov 12, 2024
7c853ad
fetch github stars count (#1084)
Lendemor Nov 12, 2024
e2ffcac
Optimize AG Grid state (#1082)
masenf Nov 13, 2024
5f9d91c
Add missing ``` at end of project-structure.md code box (#1088)
pablofueros Nov 14, 2024
d1bacd5
changelog for 0.6.5 (#1083)
masenf Nov 15, 2024
0e7e484
Add llms.txt (#1095)
Alek99 Nov 21, 2024
dbb5d48
[ENG-4127][ENG-4128][ENG-4126][ENG-4125]UI overview section improveme…
ElijahAhianyo Nov 21, 2024
6a2c62f
More shiki cleanup (#1093)
ElijahAhianyo Nov 21, 2024
d385796
skip compile for backend only (#1092)
Lendemor Nov 21, 2024
47698e0
Update pseudo style prop for chakra ui link (#1096)
jdvala Nov 21, 2024
e08371b
merge main
Nov 25, 2024
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
Binary file added assets/case_studies/ansa_app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions assets/customers/dark/ansa/ansa_middle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/customers/dark/ansa/ansa_small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/customers/dark/ansa/ansa_top.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions assets/customers/light/ansa/ansa_middle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/customers/light/ansa/ansa_small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/customers/light/ansa/ansa_top.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
181 changes: 181 additions & 0 deletions case-studies/ansa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
company: Ansa
description: "Why Ansa chose Reflex over a no-code/low-code framework for their workflow automations"
domain: "https://www.ansa.co"
founded: "New York, 2021"
investors: ""
stats: [
{
"metric": "Companies scored every month",
"value": "16,000"
},
{
"metric": "Core company workflows optimized",
"value": "8"
},
{
"metric": "Hours of manual work saved a month",
"value": "100+"
}
]
meta: [
{
"name": "keywords",
"content": "
python web app,
anvil,
python anvil,
anvil python,
low code python web app,
no code python web app,
venture capital python,
python web app framework,
python web apps,
web app framework python,
web app python,
"
}
]
---

```python exec
import reflex as rx
from reflex_image_zoom import image_zoom
```

```python eval
rx.vstack(
image_zoom(rx.image(src="/case_studies/ansa_app.png", border_radius="10px", alt="Ansa App")),
rx.text("Ansa App built with Reflex"),
width="100%",
)
```

Meet [Ansa](www.ansa.co), a venture capital firm based in New York City that invests in companies from Series A to C. They have invested in companies like Defense Unicorns, Bland, Gradient, and Selector and prior to founding the firm, supported investments in many of the venture-capital industry’s largest outcomes including Crowdstrike, Coinbase, and SurveyMonkey to name a few.

Ryan Sullivan is an investor and oversees the engineering and data science team at Ansa. He finds and supports new investments and is the architect behind Ansa's data-driven sourcing strategy, working closely with the firms’ Managing Partner Marco Demeireles to build the firms’ proprietary sourcing applications and research products.


## Ansa's data analysis challenge

Ansa has an investable universe of 10s of thousands of companies and they need to make sure they are spending time with the right companies at the right time. Their sourcing is thesis driven, so they need to both quickly find all companies in a theme of interest and track opportunities across the broader market. With a lean investment team, they need to leverage software and data to review and track all these opportunities.

Ryan and his team's goal was to automate and augment as much as they could on this company sourcing and review workflow. This included helping their lean investment team be more efficient and effective in finding interesting companies and reaching out to them. They also began using Data Science and Machine Learning to help surface more relevant companies leverage proprietary and third party data sources.

```md quote
- name: Ryan
- role: Investor and Head of Data
We have a lean investment team and there's a lot of opportunities out there, so we're trying to automate as much as we can on the workflow side to help our team be efficient and research, review, and reach out to as many companies as possible.
```

They wanted custom tools to give them an edge, such as an in-house scoring model to help flag important companies.


## How Ansa hit the limits of a low-code python framework

Ryan and his team wanted to build a web interface so the broader team could run these automations to automate their manual workflows. They wanted a pure python solution as this was the language the team was most familiar with.

```md quote
- name: Ryan
- role: Investor and Head of Data
We don't have a full engineering team, so to build a full web app from scratch seemed like a lot to manage. In addition, our team is mostly data engineers / analysts so we are far more comfortable with Python than JavaScript.
```

The team previously built on an all-python, low-code / no code framework. They didn't like the aesthetic and wanted to use more modern looking React components.

```md quote
- name: Ryan
- role: Investor and Head of Data
It’s an older framework and the components didn't look that good. We wanted to use react components and just make it look a little bit more modern.
```

Their main concern though was that they didn't want to outgrow a near no code framework, as they wanted to build their app for the long term.


```md quote
- name: Ryan
- role: Investor and Head of Data
We don't want to run into a situation where this year or next year, we want to add more functionality that this low code framework doesn't have and we're not able to integrate it. Additionally, the rate of improvement and development velocity from the Reflex team gave us confidence that their offering would continue to improve over time. We're building this for the long term and we want to make sure we both have the flexibility to not outgrow it and are working with the best out there.
```

In addition, there were particular technologies like LLMs and Vector Databases that Ryan and the team knew at some point they would want to integrate into the app. It would be extremely difficult if not impossible to keep up with these latest innovations with low/no code frameworks.


```md quote
- name: Ryan
- role: Investor and Head of Data
I started to feel that with this framework I didn’t know if they could keep up with the pace of new developments with LLMs. They abstract a lot of the backend, so it's difficult to install third party libaries and you don't have full control over the database. For example some of the newer stuff we do with vector databases, embeddings models, or LLMs would be harder to do with this framework as we'd have to move off their native database.
```


## From manual work to automation with Reflex

Ansa switched to Reflex so they could build an app for the long term and accommodate all the latest innovations in LLM development without needing any JavaScript.

They currently have an app with 8 different core company workflow automations, several of which we will discuss in this case study.

The main challenges that Ansa faces are one, figuring out what companies, out of the 10s of thousands within their investment mandate, they should be investigating further, and two, automating all the manual data collection and work required to reach out.


### Creating natural language company search

The first automated workflow they built, using a combination of OpenAI, Langchain, and Chroma, introduced vector and natural language searches over their database. This allows employees to combine quantitative and strict filtering with an understanding of the companies product offering through vector similarity. For example, an employee can type "Carbon accounting software companies with a CEO in NYC that score over 60" and receive a curated list of companies that fit that description.

```md quote
- name: Ryan
- role: Investor and Head of Data
We use LLMs to help navigate through the companies site and find different details. For example the customer page for one website, may be different from another. The LLM then summarizes all that data and creates embeddings on them and then we use that for the searches. The LLMs help us normalize across different companies, even if pages are named differently, so we can easily search through all of them and figure out what the company does.
```

### In-house company scoring algorithm

The next automated workflow takes this list of companies and scores them. With private companies, there is far less data available to assess fit than with public companies, so they rely on alternative data to power a scoring algorithm that assesses the probability a given company is a fit for their investment workflow. They proactively score ~15K companies and display them in Reflex, and also built another automated workflow to score ad-hoc lists of companies. This workflow can take in a list in any format and send the identifiers to their API where they are scored by their custom ML model hosted in Databricks.

The scored data is then displayed to the user in Reflex and emailed to the user as a CSV. This ML model is trained on a labeled dataset they have curated over years, and spots combinations of factors that they believe will lead to successful investments for the fund.


### Improving email outbound

Finally, when their team has a short list of companies that fit within an investment, they built a third workflow to automate the extraction of the relevant information to reach out to these companies. Ryan runs us through this final workflow in his quote below.


```md quote
- name: Ryan
- role: Investor and Head of Data
Let’s say we have 30 companies that we want to email. How can you efficiently send a custom note to each of these companies and track it properly? We launch a script, that runs through a Reflex background event, that'll go through each company, check the CRM ownership, fill out relevant fields and find the best person to reach out to. A lot of times, especially with early stage companies, data is missing or partially complete. So this workflow will leverage LLMs throughout the process to handle fuzzy matching and make contextual decisions, as well as proactively summarize company content, news, and relevant Ansa content to help support the email writing. Before we would do this all manually, now with this new workflow in Reflex, we've taken what was once 30+ clicks across 5 different apps and made it 5x faster with 2 clicks across 2 apps.
```

All these different workflows are now built into a single Reflex app. It makes it extremely easy for anyone on the team to run any of these workflows and leverage LLM-powered automation with a few clicks.

Throughout building this Reflex app, Ryan used:

- Supabase database to store all their data

- LLM tools like OpenAI, Tavily, Browserbase, Langchain, and Chroma

- Google Auth login component for Ansa employees to log in

- AG Grid Table Component

- Download Functionality


Overall Ansa found that with Reflex, as everything is in pure python, they were able to integrate everything they wanted and knew they always could incorporate new tech, which was a concern with their previous framework.


```md quote
- name: Ryan
- role: Investor and Head of Data
Reflex was a better fit overall. Given that it's just python code, I'm always comfortable that we'll be able use different tools and to figure out how to make it work with Reflex versus being stuck with the integrations that our old solution had.
```


## What Ansa gained from using Reflex

The app that Ryan and his team created, which contains 8 different automated workflows, is now a central dependency for Ansa to source potential companies and analyze them.

```md quote
- name: Ryan
- role: Investor and Head of Data
75% of our team uses the app on a weekly basis and we estimate we're saving over ~100 team hours per month.
```
2 changes: 1 addition & 1 deletion pcweb/pages/customers/data/customers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def get_route(path: str):
investors=document.metadata["investors"],
stats=document.metadata["stats"],
meta=document.metadata["meta"],
)(lambda doc=document: content(document))
)(lambda doc=document: content(doc))

# # Add the route to the list of routes.
customers_routes.append(comp)
11 changes: 8 additions & 3 deletions pcweb/pages/customers/views/bento_cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def card(company: str, text: str) -> rx.Component:
),
alt=f"{company} logo",
loading="lazy",
class_name="absolute top-10 left-10 z-[2]",
class_name="absolute top-10 left-10 z-[2] max-h-[32px]",
),
# Center company logo
rx.image(
Expand All @@ -21,7 +21,7 @@ def card(company: str, text: str) -> rx.Component:
),
alt=f"{company} small logo",
loading="lazy",
class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2]",
class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px]",
),
# Wave pattern
rx.html(
Expand Down Expand Up @@ -93,5 +93,10 @@ def bento_cards() -> rx.Component:
company="bayesline",
text="Why Basyesline Chose Reflex over Plotly Dash",
),
class_name="grid grid-cols-1 lg:grid-cols-1 gap-4 mx-auto w-full max-w-[69.25rem]",
# Ansa
card(
company="ansa",
text="Why Ansa chose Reflex over a no-code/low-code framework for their workflow automations",
),
class_name="grid grid-cols-1 lg:grid-cols-2 gap-4 mx-auto w-full max-w-[69.25rem]",
)
2 changes: 2 additions & 0 deletions pcweb/pages/customers/views/customers_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ def customers_list() -> rx.Component:
customers_list_item("AutoDesk", "https://www.autodesk.com/", "SaaS"),
# Bayesline
customers_list_item("Bayesline", "/customers/bayesline", "AI", True),
# Ansa
customers_list_item("Ansa", "/customers/ansa", "Dev Tools", True),
# Your company
your_company_item("Your company", getting_started.introduction.path, ""),
class_name="flex flex-col max-w-[40rem] justify-center w-full items-center",
Expand Down
72 changes: 72 additions & 0 deletions pcweb/styles/tailwind_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,78 @@
},
],
},
"fontSize": {
"xs": [
"0.75rem",
{
"lineHeight": "1rem",
"letterSpacing": "-0.00375rem",
},
],
"sm": [
"0.875rem",
{
"lineHeight": "1.25rem",
"letterSpacing": "-0.01rem",
},
],
"base": [
"1rem",
{
"lineHeight": "1.5rem",
"letterSpacing": "-0.015rem",
},
],
"lg": [
"1.125rem",
{
"lineHeight": "1.625rem",
"letterSpacing": "-0.01625rem",
},
],
"xl": [
"1.25rem",
{
"lineHeight": "1.75rem",
"letterSpacing": "-0.028125rem",
},
],
"2xl": [
"1.5rem",
{
"lineHeight": "2rem",
"letterSpacing": "-0.0375rem",
},
],
"3xl": [
"2rem",
{
"lineHeight": "2.5rem",
"letterSpacing": "-0.07rem",
},
],
"4xl": [
"2.5rem",
{
"lineHeight": "3rem",
"letterSpacing": "-0.125rem",
},
],
"5xl": [
"3rem",
{
"lineHeight": "3.5rem",
"letterSpacing": "-0.1575rem",
},
],
"6xl": [
"3.5rem",
{
"lineHeight": "4rem",
"letterSpacing": "-0.1925rem",
},
],
},
"colors": {
**radix_colors_dict,
**custom_colors_dict,
Expand Down
11 changes: 7 additions & 4 deletions pcweb/templates/storypage.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,13 @@ def company_card(company: str, founded: str, investors: str, url: str) -> rx.Com
class_name="flex flex-col",
),
# Investors
rx.box(
rx.text("Investors", class_name="text-slate-9 font-small-smbold"),
rx.text(investors, class_name="text-slate-12 font-base truncate"),
class_name="flex flex-col",
rx.cond(
investors,
rx.box(
rx.text("Investors", class_name="text-slate-9 font-small-smbold"),
rx.text(investors, class_name="text-slate-12 font-base truncate"),
class_name="flex flex-col",
),
),
class_name="flex-col gap-4 w-[13rem] p-8 rounded-[1.125rem] border border-slate-3 bg-slate-2 z-[1] justify-start absolute right-[-6.5rem] top-[12rem] hidden xl:flex",
is_external=True,
Expand Down